diff --git a/CHANGELOG b/CHANGELOG index 258e8c6..a15fee1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Version - 8.5.0 * Added Solus OS * Added Ka OS * Added PC Unlocker +* Added Acronis True Image * Fixed bug - Same drives appearing multiple times under imager tab * Fixed HBCD boot issue * Fix crash when using ISO Imager option to write ISO files (fix by bagage) diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..2e3fe23 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,74 @@ +# file GENERATED by distutils, do NOT edit +multibootusb +multibootusb-pkexec +org.debian.pkexec.run-multibootusb.policy +setup.py +data/multibootusb.desktop +data/version.txt +data/multibootusb/bg.png +data/multibootusb/chain.c32 +data/multibootusb/extlinux.cfg +data/multibootusb/grub.exe +data/multibootusb/memdisk +data/multibootusb/menu.c32 +data/multibootusb/menu.lst +data/multibootusb/syslinux.cfg +data/multibootusb/vesamenu.c32 +data/tools/mbr.bin +data/tools/multibootusb.png +data/tools/dd/dd.exe +data/tools/dd/diskio.dll +data/tools/mkfs/mke2fs.exe +data/tools/syslinux/syslinux_linux.zip +data/tools/syslinux/syslinux_linux_64.zip +data/tools/syslinux/syslinux_modules.zip +data/tools/syslinux/syslinux_windows.zip +scripts/_7zip.py +scripts/__init__.py +scripts/admin.py +scripts/config.py +scripts/distro.py +scripts/gen.py +scripts/imager.py +scripts/install.py +scripts/iso.py +scripts/isodump3.py +scripts/mbusb_cli.py +scripts/mbusb_gui.py +scripts/persistence.py +scripts/qemu.py +scripts/syslinux.py +scripts/udisks.py +scripts/uninstall_distro.py +scripts/update_cfg_file.py +scripts/usb.py +scripts/gui/__init__.py +scripts/gui/ui_multibootusb.py +scripts/progressbar/__init__.py +scripts/progressbar/compat.py +scripts/progressbar/progressbar.py +scripts/progressbar/widgets.py +scripts/pyudev/__init__.py +scripts/pyudev/_compat.py +scripts/pyudev/_errors.py +scripts/pyudev/_qt_base.py +scripts/pyudev/_util.py +scripts/pyudev/core.py +scripts/pyudev/discover.py +scripts/pyudev/glib.py +scripts/pyudev/monitor.py +scripts/pyudev/pyqt4.py +scripts/pyudev/pyqt5.py +scripts/pyudev/pyside.py +scripts/pyudev/version.py +scripts/pyudev/wx.py +scripts/pyudev/_ctypeslib/__init__.py +scripts/pyudev/_ctypeslib/_errorcheckers.py +scripts/pyudev/_ctypeslib/libc.py +scripts/pyudev/_ctypeslib/libudev.py +scripts/pyudev/_ctypeslib/utils.py +scripts/pyudev/_os/__init__.py +scripts/pyudev/_os/pipe.py +scripts/pyudev/_os/poll.py +scripts/pyudev/device/__init__.py +scripts/pyudev/device/_device.py diff --git a/deb_dist/multibootusb-8.5.0/PKG-INFO b/deb_dist/multibootusb-8.5.0/PKG-INFO new file mode 100644 index 0000000..de37e36 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: multibootusb +Version: 8.5.0 +Summary: Create multi boot live Linux on a USB disk... +Home-page: http://multibootusb.org/ +Author: Sundar +Author-email: feedback.multibootusb@gmail.com +License: General Public License (GPL) +Description: multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives. +Platform: Linux diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb.desktop b/deb_dist/multibootusb-8.5.0/data/multibootusb.desktop new file mode 100644 index 0000000..e17d8b8 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/data/multibootusb.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=multibootusb +Comment=Install multiple Linux Operating System on USB +Icon=multibootusb +Exec=multibootusb-pkexec +Categories=System; +StartupNotify=true diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/bg.png b/deb_dist/multibootusb-8.5.0/data/multibootusb/bg.png new file mode 100644 index 0000000..629ed91 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/bg.png differ diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/chain.c32 b/deb_dist/multibootusb-8.5.0/data/multibootusb/chain.c32 new file mode 100644 index 0000000..52d4c52 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/chain.c32 differ diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/extlinux.cfg b/deb_dist/multibootusb-8.5.0/data/multibootusb/extlinux.cfg new file mode 100644 index 0000000..e69de29 diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/grub.exe b/deb_dist/multibootusb-8.5.0/data/multibootusb/grub.exe new file mode 100644 index 0000000..57b890d Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/grub.exe differ diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/memdisk b/deb_dist/multibootusb-8.5.0/data/multibootusb/memdisk new file mode 100644 index 0000000..411c07d Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/memdisk differ diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.c32 b/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.c32 new file mode 100644 index 0000000..90318f0 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.c32 differ diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.lst b/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.lst new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/data/multibootusb/menu.lst @@ -0,0 +1 @@ + diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/syslinux.cfg b/deb_dist/multibootusb-8.5.0/data/multibootusb/syslinux.cfg new file mode 100644 index 0000000..445bd5f --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/data/multibootusb/syslinux.cfg @@ -0,0 +1,35 @@ +# This file is created by MultiBootUSB. +default vesamenu.c32 +prompt 0 +menu title MultiBootUSB +MENU BACKGROUND /multibootusb/bg.png +TIMEOUT 300 +MENU WIDTH 80 +MENU MARGIN 10 +MENU PASSWORDMARGIN 3 +MENU ROWS 12 +MENU TABMSGROW 18 +MENU CMDLINEROW 18 +MENU ENDROW -1 +MENU PASSWORDROW 11 +MENU TIMEOUTROW 20 +MENU HELPMSGROW 22 +MENU HELPMSGENDROW -1 +MENU HIDDENROW -2 +MENU HSHIFT 0 +MENU VSHIFT 0 +MENU COLOR border 30;44 #40ffffff #a0000000 std +MENU COLOR title 1;36;44 #9033ccff #a0000000 std +MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all +MENU COLOR unsel 37;44 #50ffffff #a0000000 std +MENU COLOR help 37;40 #c0ffffff #a0000000 std +MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std +MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std +MENU COLOR msg07 37;40 #90ffffff #a0000000 std +MENU COLOR tabmsg 31;40 #30ffffff #00000000 std +label Boot from Hard Drive +MENU LABEL Boot from Hard Disk +KERNEL chain.c32 +APPEND hd1 +MENU DEFAULT + diff --git a/deb_dist/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 b/deb_dist/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 new file mode 100644 index 0000000..a92f4fe Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/dd/dd.exe b/deb_dist/multibootusb-8.5.0/data/tools/dd/dd.exe new file mode 100644 index 0000000..8cf719c Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/dd/dd.exe differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/dd/diskio.dll b/deb_dist/multibootusb-8.5.0/data/tools/dd/diskio.dll new file mode 100644 index 0000000..1c6a7f0 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/dd/diskio.dll differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/mbr.bin b/deb_dist/multibootusb-8.5.0/data/tools/mbr.bin new file mode 100644 index 0000000..646a684 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/mbr.bin differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe b/deb_dist/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe new file mode 100644 index 0000000..79690fd Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/multibootusb.png b/deb_dist/multibootusb-8.5.0/data/tools/multibootusb.png new file mode 100644 index 0000000..92c2e82 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/multibootusb.png differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip new file mode 100644 index 0000000..f16165d Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip new file mode 100644 index 0000000..7f84c74 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip new file mode 100644 index 0000000..b2a4fa5 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip differ diff --git a/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip new file mode 100644 index 0000000..3cb5897 Binary files /dev/null and b/deb_dist/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip differ diff --git a/deb_dist/multibootusb-8.5.0/data/version.txt b/deb_dist/multibootusb-8.5.0/data/version.txt new file mode 100644 index 0000000..6d28907 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/data/version.txt @@ -0,0 +1 @@ +8.5.0 diff --git a/deb_dist/multibootusb-8.5.0/debian/changelog b/deb_dist/multibootusb-8.5.0/debian/changelog new file mode 100644 index 0000000..6f46594 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/debian/changelog @@ -0,0 +1,5 @@ +multibootusb (8.5.0-1) unstable; urgency=low + + * source package automatically created by stdeb 0.8.2 + + -- Sundar Sun, 05 Feb 2017 15:32:06 +0530 diff --git a/deb_dist/multibootusb-8.5.0/debian/compat b/deb_dist/multibootusb-8.5.0/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/debian/compat @@ -0,0 +1 @@ +9 diff --git a/deb_dist/multibootusb-8.5.0/debian/control b/deb_dist/multibootusb-8.5.0/debian/control new file mode 100644 index 0000000..dae68cd --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/debian/control @@ -0,0 +1,14 @@ +Source: multibootusb +Maintainer: Sundar +Section: system +Priority: optional +Build-Depends: python3-all, debhelper (>= 9), python3-all +Standards-Version: 3.9.1 +X-Python-Version: = 3.5 + +Package: python3-multibootusb +Architecture: all +Depends: ${misc:Depends}, ${python3:Depends}, python3-pyqt5, parted, util-linux, mtools, python3-dbus, p7zip-full, python3-six +Description: Create multi boot live Linux on a USB disk... + multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives. + diff --git a/deb_dist/multibootusb-8.5.0/debian/rules b/deb_dist/multibootusb-8.5.0/debian/rules new file mode 100644 index 0000000..2ddadd9 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +# This file was automatically generated by stdeb 0.8.2 at +# Sun, 05 Feb 2017 15:32:06 +0530 +export PYBUILD_NAME=multibootusb +%: + dh $@ --with python3 --buildsystem=pybuild + diff --git a/deb_dist/multibootusb-8.5.0/debian/source/format b/deb_dist/multibootusb-8.5.0/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/deb_dist/multibootusb-8.5.0/multibootusb b/deb_dist/multibootusb-8.5.0/multibootusb new file mode 100644 index 0000000..974726e --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/multibootusb @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: multibootusb +# Purpose: Main file which will determine if cli or gui is to be opened +# 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 logging +import getopt +import sys +import os +import platform + +# Had trouble in importing scripts directory. Had to add few lines below to ensure it works on source as well as +# post install +try: + from scripts.mbusb_cli import * + from scripts import admin + from scripts import gen +except: + try: + from .scripts.mbusb_cli import * + from .scripts import admin + from .scripts import gen + except: + import scripts + +gui = True +iso_link = None +usb_disk = None +uninstall = False + +def usage(): + print('\nAn advanced multi boot live usb creator using command line.') + print('\nUsage: python3 multibootusb -c -i /path/to/iso [option(s)] -t /path/to/device\n') + print('[option(s)] are :\n') + print(' -h or --help : Print this help message and exit') + print(' -i or --iso : Path to ISO file') + print(' -t or --target : Path to target USB device partition (example /dev/sdb1)\n') + print(' -c or --command : This option is must for invoking multibootusb from command line\n') + print(' -u or --uninstall : List and uninstall distro from USB disk') + print(' Command line example for making a bootable USB from command line should look like this:-\n') + print(' python3 multibootusb -c -i ../../favourite.iso -t /dev/sdb1\n') + exit(2) + + +def start_gui(): + from scripts import mbusb_gui + gen.log('Starting multibootusb GUI...') + if platform.system() == 'Linux': + if os.getuid() != 0: + gen.log('\n\nAdmin privilege is required to run multibootusb.\n If you are running from source try ' + '\'sudo python3 ./multibootusb\'\n or you can try \'multibootusb-pkexec\' (post install)\n\n', + info=False, debug=True) + mbusb_gui.main_gui() + + +if __name__ == '__main__': + if platform.system() == 'Windows': + if not admin.isUserAdmin(): + admin.runAsAdmin() + sys.exit(0) + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:t:vhcu', + ['iso=', 'target=', 'version', 'help', 'command', 'uninstall']) + except getopt.GetoptError: + usage() + sys.exit(2) + for opt, arg in opts: + if opt in ('-h', '--help'): + usage() + sys.exit(2) + elif opt in ('-v', '--version'): + print_version() + sys.exit() + elif opt in ('-i', '--iso'): + config.iso_link = arg + elif opt in ('-t', '--target'): + config.usb_disk = arg + elif opt in ('-c', '--command'): + gui = False + elif opt in ('-u', '--uninstall'): + uninstall = True + else: + gui = True + #start_gui() + ''' + usage() + sys.exit() + ''' + +if gui is False: + if uninstall is True and config.usb_disk is not None: + cli_uninstall_distro() + elif config.iso_link is None and config.usb_disk is None: + usage() + elif config.iso_link is None or config.usb_disk is None: + print('\nOptions \'-i\' and \'t\' must be supplied together. See the usage below.') + usage() + else: + cli_install_distro() + +elif gui is True: + start_gui() diff --git a/deb_dist/multibootusb-8.5.0/multibootusb-pkexec b/deb_dist/multibootusb-8.5.0/multibootusb-pkexec new file mode 100644 index 0000000..f9f7e98 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/multibootusb-pkexec @@ -0,0 +1,6 @@ +#!/bin/bash +if [ $(which pkexec) ]; then + pkexec --disable-internal-agent "/usr/bin/multibootusb" "$@" +else + /usr/bin/multibootusb "$@" +fi \ No newline at end of file diff --git a/deb_dist/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy b/deb_dist/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy new file mode 100644 index 0000000..e111748 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy @@ -0,0 +1,19 @@ + + + + + + Run the multibootusb program + Authentication is required to run the multibootusb + + auth_admin + auth_admin + auth_admin + + /usr/bin/multibootusb + true + + + diff --git a/deb_dist/multibootusb-8.5.0/scripts/_7zip.py b/deb_dist/multibootusb-8.5.0/scripts/_7zip.py new file mode 100644 index 0000000..43f646a --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/_7zip.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: 7zip.py +# Purpose: Wrapper module to list and extract ISO files using 7zip +# 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 platform +import subprocess +from . import config +from . import gen + +if platform.system() == 'Windows': + _7zip = gen.quote(gen.resource_path(os.path.join('data', 'tools', '7zip', '7z.exe'))) +else: + _7zip = '7z' + + +def extract_iso(src, dst, pattern=None, suppress_out=True): + """ + Simple wrapper function to extract ISO file to destination + :param src: Path to ISO file + :param dst: Path to directory where the files are to be extracted + :param patter: The pattern to match the files to be extracted + :return: + """ + # 7z x -y -oC:\path_to_directory X:\path_to_iso_file.iso + # 7z e archive.zip -oC:\path_to_directory *.cfg *.bin -r + if platform.system() == 'Windows': + cli_option = ' -bb1' # Linux does not accept this option (may be due to version diff). + if suppress_out != '': + # suppress_out = ' 2> nul' + suppress_out = '' + else: + cli_option = '' + if suppress_out != '': + suppress_out = ' 2> /dev/null' + + if not os.path.exists(src): + gen.log('ISO file could not be found on the location specified.') + return False + if not os.path.exists(dst): + os.makedirs(dst, exist_ok=True) + + if pattern is None: + _cmd = _7zip + cli_option + ' x -y -o' + gen.quote(dst) + ' ' + gen.quote(src) + suppress_out + else: + _cmd = _7zip + ' -y x ' + gen.quote(src) + ' -o' + dst + ' ' + gen.quote(pattern) + ' -r' + suppress_out + # gen.log('Executing', _cmd) + _7zip_process = subprocess.Popen(_cmd, universal_newlines=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + config.status_text = 'Extracting ' + os.path.basename(src) + while True: + line = _7zip_process.stdout.readline() + # gen.log(line) + if line.startswith('- '): + config.status_text = 'Extracting ' + line[2:] + elif platform.system() == 'Linux': # line.startswith('Extracting'): # Under Linux it prints directly all the process (may be due to version diff). + config.status_text = line + if line == '' and _7zip_process.poll() != None: + break + + +def list_iso(iso_link, suppress_out=True): + if platform.system() == 'Windows': + if suppress_out is True: + suppress_out = ' 2> nul' + else: + if suppress_out is True: + suppress_out = ' 2> /dev/null' + if not os.path.exists(iso_link): + gen.log('Path to ISO link does not exist.') + return False + else: + file_list = [] + _cmd = _7zip + ' l ' + gen.quote(iso_link) + suppress_out + # gen.log('Executing', _cmd) + _cmd_out = subprocess.check_output(_cmd, universal_newlines=True, stderr=subprocess.PIPE, shell=True).splitlines() + for line in _cmd_out: + line = line.split() + if '.....' in line: + file_list.append(line[-1]) + + return file_list + + +def test_iso(iso_link, suppress_out=True): + """ + Function to test if ISO file is corrupted. Relying only on 7zip. + :param iso_link: Path to ISO file + :return: True if test is positive + """ + # 7z t /path/to/iso/file.iso + # return value : 0 No error + # return value : 1 Warning (Non fatal error(s)) + # return value : 2 Fatal error + # return value : 7 Command line error + # return value : 8 Not enough memory for operation + # return value : 255 User stopped the process + + if platform.system() == 'Windows': + if suppress_out is True: + suppress_out = ' > nul' + else: + if suppress_out is True: + suppress_out = ' > /dev/null' + + _cmd = _7zip + ' t ' + iso_link + suppress_out + + # gen.log('Executing', _cmd) + + rc = subprocess.call(_cmd, shell=True) + + if rc == 0 or rc == 1: + return True + else: + return False + + +if __name__ == '__main__': + # slitaz-4.0.iso + # ubuntu-16.04-desktop-amd64.iso + # avg_arl_cdi_all_120_160420a12074.iso + # haiku-nightly.iso + # Hiren_BootCD.iso + file_list = list_iso('../../ubuntu_14_04_backup/Downloads/clonezilla-live-2.4.2-32-amd64.iso') + for f in file_list: + print(f) diff --git a/deb_dist/multibootusb-8.5.0/scripts/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/__init__.py new file mode 100644 index 0000000..c5c69ae --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/__init__.py @@ -0,0 +1,10 @@ +# Author(s) information +__authors__ = ['Sundar'] +__author__ = ','.join(__authors__) +__credits__ = ['Ian Bruce', 'Lee'] +__copyright__ = 'Copyright (c) 2014' +__license__ = 'GPL' + +# Maintanence information +__maintainer__ = 'Sundar' +__email__ = 'feedback.multibootusb@gmail.com' diff --git a/deb_dist/multibootusb-8.5.0/scripts/admin.py b/deb_dist/multibootusb-8.5.0/scripts/admin.py new file mode 100644 index 0000000..9e280f1 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/admin.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4 +# Name: admin.py +# Purpose: Module to ask for admin rights under Linux and Windows +# Authors: Originally developed by Preston Landers (for windows) and modified for multibootusb by Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of the same license as Python 2.6.5 + +## +## (C) COPYRIGHT © Preston Landers 2010 +## Released under the same license as Python 2.6.5 +## + +""" +User Access Control for Microsoft Windows Vista and higher. This is +only for the Windows platform. + +This will relaunch either the current script - with all the same command +line parameters - or else you can provide a different script/program to +run. If the current user doesn't normally have admin rights, he'll be +prompted for an admin password. Otherwise he just gets the UAC prompt. + +This is meant to be used something like this:: + + if not pyuac.isUserAdmin(): + return pyuac.runAsAdmin() + + # otherwise carry on doing whatever... + +See L{runAsAdmin} for the main interface. + +""" +import os +import traceback +import types +import platform +import sys +import subprocess +from PyQt5 import QtWidgets +from . import gen + + +def isUserAdmin(): + """ + @return: True if the current user is an 'Admin' whatever that means + (root on Unix), otherwise False. + + Warning: The inner function fails unless you have Windows XP SP2 or + higher. The failure causes a traceback to be gen.loged and this + function to return False. + """ + + if platform.system() == "Windows": + import ctypes + # WARNING: requires Windows XP SP2 or higher! + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + traceback.print_exc() + gen.log("Admin check failed, assuming not an admin.") + return False + elif platform.system() == "Linux": + return os.getuid() == 0 + else: + raise RuntimeError("Unsupported operating system for this module: %s" % (os.name,)) + + +def runAsAdmin(cmdLine=None, wait=True): + """ + Attempt to relaunch the current script as an admin using the same + command line parameters. Pass cmdLine in to override and set a new + command. It must be a list of [command, arg1, arg2...] format. + + Set wait to False to avoid waiting for the sub-process to finish. You + will not be able to fetch the exit code of the process if wait is + False. + + Returns the sub-process return code, unless wait is False in which + case it returns None. + + @WARNING: this function only works on Windows. + """ + + #if os.name == 'nt': + # raise RuntimeError, "This function is only implemented on Windows." + if platform.system() == "Windows": + + import win32api, win32con, win32event, win32process + from win32com.shell.shell import ShellExecuteEx + from win32com.shell import shellcon + + python_exe = sys.executable + + if cmdLine is None: + cmdLine = [python_exe] + sys.argv + elif type(cmdLine) not in (types.TupleType, types.ListType): + raise ValueError("cmdLine is not a sequence.") + cmd = '"%s"' % (cmdLine[0],) + # XXX TODO: isn't there a function or something we can call to massage command line params? + params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]]) + cmdDir = '' + showCmd = win32con.SW_SHOWNORMAL + #showCmd = win32con.SW_HIDE + lpVerb = 'runas' # causes UAC elevation prompt. + + #gen.log("Running", cmd, params) + + # ShellExecute() doesn't seem to allow us to fetch the PID or handle + # of the process, so we can't get anything useful from it. Therefore + # the more complex ShellExecuteEx() must be used. + + # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd) + + procInfo = ShellExecuteEx(nShow=showCmd, + fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, + lpVerb=lpVerb, + lpFile=cmd, + lpParameters=params) + + if wait: + procHandle = procInfo['hProcess'] + obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE) + rc = win32process.GetExitCodeProcess(procHandle) + #gen.log "Process handle %s returned code %s" % (procHandle, rc) + else: + rc = None + + return rc + + +def adminCmd(cmd, fork=False, gui=False): + """ + This simple function checks for a sudo command and runs a command using it. + This function tries to launch given script with root access using pkexec/gksu/gksudo/kdesu/kdesudo, + if one of them is already installed. + PyQt4 is used as GUI. + Author : sundar + """ + sudo_cmd = '' + if os.getuid() == 0: + sudo_cmd = cmd + else: + if os.system('which pkexec') == 0: + if gui: + cmd = ['export DISPLAY=$DISPLAY; export XAUTHORITY=$XAUTHORITY; '] + cmd # By default, pkexec disallows X11 apps. Restore DISPLAY & XAUTHORITY to allow it. man 1 pkexec/"SECURITY NOTES" section + sudo_cmd = ['pkexec', '/bin/sh', '-c'] + elif os.system('which gksudo') == 0: + sudo_cmd = ["gksudo", "--", "/bin/sh", "-c"] + elif os.system('which gksu') == 0: + sudo_cmd = ["gksu"] + elif os.system('which kdesudo') == 0: + sudo_cmd = ["kdesudo", "-t", "-c"] # http://www.unix.com/man-page/debian/1/kdesudo/ + elif os.system('which kdesu') == 0: + sudo_cmd = ["kdesu", "-t", "-c"] # http://linux.die.net/man/1/kdesu + else: + QtWidgets.QMessageBox.information('No root...', + 'Could not find any of: pkexec, sudo, gksu, kdesu, gksudo, or kdesudo. Please install one then restart multibootusb.') + sys.exit(0) + final_cmd = ' '.join(sudo_cmd + ['"' + ' '.join(cmd).replace('"', '\\"') + '"']) + gen.log("Executing ==> " + final_cmd) + if fork: + return subprocess.Popen(final_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, shell=True) + else: + ret = subprocess.call(final_cmd, shell=True) + gen.log("Process returned ==> " + str(ret)) + return ret diff --git a/deb_dist/multibootusb-8.5.0/scripts/config.py b/deb_dist/multibootusb-8.5.0/scripts/config.py new file mode 100644 index 0000000..e80a187 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/config.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# Name: config.py +# Purpose: Module to share important variables between various modules. Mainly included so as not to call many +# functions again and again +# 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 + +iso_link = "" +usb_disk = "" +usb_mount = "" +usb_uuid = "" +usb_label = "" +persistence = 0 +distro = "" +status_text = "" +percentage = 0 +syslinux_version = '' +uninstall_distro_dir_name = "" +uninstall_distro_dir_path = "" +iso_file_list = '' + +process_exist = None + +imager_iso_link = "" +imager_usb_disk_selected = "" +imager_lock = "" +imager_percentage = "" +imager_status_text = "" + +install_size = "" + +editors_linux = ["gedit", "kate", "kwrite"] +editors_win = ["notepad++.exe", "notepad.exe"] + +imager_usb_disk = [] diff --git a/deb_dist/multibootusb-8.5.0/scripts/distro.py b/deb_dist/multibootusb-8.5.0/scripts/distro.py new file mode 100644 index 0000000..0c788ec --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/distro.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: distro.py +# Purpose: Module to detect if distro types supported by multibootusb (by extracting specific files) +# 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 string +import platform +import re +from .iso import * +from .isodump3 import ISO9660 +from .gen import * +from . import _7zip +from . import config + + +def distro(iso_cfg_ext_dir, iso_link): + """ + Detect if distro is supported by multibootusb. + :param iso_cfg_ext_dir: Directory where *.cfg files are extracted. + :return: Detected distro name as string. + """ + iso9660fs = ISO9660(iso_link) + # iso_file_list = iso9660fs.readDir("/") + iso_file_list = _7zip.list_iso(iso_link) + if platform.system() == "Linux" or platform.system() == "Windows": + for path, subdirs, files in os.walk(iso_cfg_ext_dir): + for name in files: + if name.endswith('.cfg') or name.endswith('.CFG') or name.endswith('.txt') or name.endswith('.TXT') \ + or name.endswith('.lst'): + try: + # errors='ignore' is required as some files also contain non utf character + string = open(os.path.join(path, name), errors='ignore').read() + except IOError: + return "Read Error." + else: + if any("f4ubcd" in s.lower() for s in iso_file_list): + return "f4ubcd" + if re.search(r'ubcd', string, re.I): + return "ubcd" + elif re.search(r'Super Grub Disk', string, re.I): + return "sgrubd2" + elif re.search(r'hbcd', string, re.I): + return "hbcd" + elif re.search(r'systemrescuecd', string, re.I): + return "systemrescuecd" + elif re.search(r'pmagic|partedmagic', string, re.I) and isolinux_bin_exist(iso_link): + return "parted-magic" + elif re.search(r'mgalive', string, re.I): # mounting fat filesystem hard coded in to initrd. + # Can be modified only under linux. + return "mageialive" + elif re.search(r'archisolabel|misolabel', string, re.I): + return "arch" + elif re.search(r'chakraisolabel', string, re.I): + return "chakra" + elif re.search(r'kdeosisolabel', string, re.I): + return "kaos" + elif re.search(r'boot=live', string, re.I) and isolinux_bin_exist(iso_link): + return "debian" + elif re.search(r'debian-installer', string, re.I) and isolinux_bin_exist(iso_link): + return "debian-install" + elif re.search(r'solydx', string, re.I): + return "solydx" + elif re.search(r'knoppix', string, re.I): + return "knoppix" + elif re.search(r'root=live:CDLABEL=', string, re.I): + return "fedora" + elif re.search(r'redhat', string, re.I): + return "redhat" + # elif re.search(r'suse', string, re.I): + # return "suse" + elif re.search(r'opensuse', string, re.I): + return "opensuse" + elif re.search( + r'slitaz|dban |ophcrack|tinycore|rescue.cpi|xpud|untangle|4mlinux|partition wizard|android-x86.png|' + r'riplinux|lebel dummy|http://pogostick.net/~pnh/ntpasswd/|AVG Rescue CD', string, re.I): + return "slitaz" + elif re.search(r'boot=casper', string, re.I): + return "ubuntu" + elif re.search(r'wifislax', string, re.I): + return "wifislax" + elif re.search(r'slax', string, re.I): + return "slax" + elif re.search(r'sms|vector|autoexec', string, re.I) and isolinux_bin_exist(iso_link): + return "sms" + elif re.search(r'antix', string, re.I): + return "antix" + elif re.search(r'porteus', string, re.I): + return "porteus" + elif re.search(r'livecd=livecd|PCLinuxOS', string, re.I): + return "pclinuxos" + elif re.search(r'looptype=squashfs', string, re.I): + return "gentoo" + elif re.search(r'finnix', string, re.I): + return "finnix" + elif re.search(r'wifiway', string, re.I): + return "wifiway" + elif re.search(r'puppy|quirky', string, re.I): + return "puppy" + elif re.search(r'ipcop', string, re.I): + return "ipcop" + elif re.search(r'ipfire', string, re.I): + return "ipfire" + elif re.search(r'zenwalk|slack|salix', string, re.I) and re.search(r'live', string, re.I): + return "salix-live" + elif re.search(r'zenwalk|slack|salix', string, re.I): + return "zenwalk" + elif re.search(r'ubuntu server', string, re.I): + return "ubuntu-server" + elif re.search(r'Welcome to CentOS', string, re.I): + return "centos-net-minimal" + elif re.search(r'Trinity Rescue Kit', string, re.I): + return "trinity-rescue" + elif re.search(r'alpine', string, re.I): + return "alpine" + elif re.search(r'http://support.kaspersky.com', string, re.I): + return "kaspersky" + elif re.search(r'ALT Linux', string, re.I): + return "alt-linux" + elif re.search(r'Sergei Strelec', string, re.I): + return "Windows" + elif re.search(r'ReactOS', string, re.I): + return "ReactOS" + elif re.search(r'fsecure', string, re.I): + return "fsecure" + elif re.search(r'default rwp', string, re.I): + return "pc-unlocker" + + + distro = detect_iso_from_file_list(iso_link) + if distro: + return distro + # FIXME: See the below comments. + ''' + else: + # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails. + # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory. + # All works fine but unable to boot the distro successfully. Also, see the generic section from + # syslinux, update_cfg and install_distro modules. + if self.isolinux_bin_exist(): + return "generic" + ''' + else: + return None + + +def detect_iso_from_file_list(iso_link): + """ + Fallback detection script from the content of an ISO. + :return: supported distro as string + """ + if os.path.exists(iso_link): + iso_file_list = _7zip.list_iso(iso_link) + if any("sources" in s.lower() for s in iso_file_list) and any("boot.wim" in s.lower() for s in iso_file_list): + return "Windows" + elif any("config.isoclient" in s.lower() for s in iso_file_list): + return "opensuse" + elif any("dban" in s.lower() for s in iso_file_list): + return "slitaz" + elif any("memtest.img" in s.lower() for s in iso_file_list): + return "mentest" + elif any("menu.lst" in s.lower() for s in iso_file_list): + return "grub4dos" + elif any("bootwiz.cfg" in s.lower() for s in iso_file_list) and any("bootmenu_logo.png" in s.lower() for s in iso_file_list): + return "grub4dos_iso" + else: + log(iso_file_list) + +if __name__ == '__main__': + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + iso_link = 'Downloads/clonezilla-live-2.4.2-32-amd64.iso' + iso_extract_file(iso_link, iso_cfg_ext_dir, 'cfg') + log(distro(iso_cfg_ext_dir)) diff --git a/deb_dist/multibootusb-8.5.0/scripts/gen.py b/deb_dist/multibootusb-8.5.0/scripts/gen.py new file mode 100644 index 0000000..461821c --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/gen.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: log.py +# Purpose: This 'general' module contain many functions required to be called at many places +# 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 logging +import sys +import os +import platform +import shutil +import string +import zipfile +import tempfile + + +def scripts_dir_path(): + return os.path.dirname(os.path.realpath(__file__)) + + +def log(message, info=True, error=False, debug=False): + """ + 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)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.DEBUG) + print(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__))) + + if os.path.exists(os.path.join(basePath, relativePath)): + path = os.path.join(basePath, relativePath) + return path + elif not os.path.exists(os.path.join(basePath, relativePath)): + if os.path.exists(os.path.join(os.path.abspath("."), relativePath)): + basePath = os.path.abspath(".") + elif os.path.exists(os.path.join(os.path.abspath(".."), relativePath)): + basePath = os.path.abspath("..") + path = os.path.join(basePath, relativePath) + return path + + +def print_version(): + """ + Simple log the version number of the multibootusb application + :return: + """ + log('multibootusb version : ', mbusb_version()) + + +def quote(text): + """ + Function to quote the input word or sentence. + :param text: Any word or sentence. + :return: Quoted text or sentence. If already quoted the same text is returned. + """ + if not is_quoted(text): + return '"' + text + '"' + else: + return text + + +def is_quoted(text): + """ + Function to check if word is quoted. + :param text: Any word or sentence with or without quote. + :return: True if text is quoted else False. + """ + if text.startswith("""") and text.endswith("""): + return True + else: + return False + + +def has_digit(word): + """ + Useful function to detect if input word contain digit. + :param word: Any alphanumeric word. + :return: True if the word has a digit else False. + """ + return any(char.isdigit() for char in word) + + +def sys_64bits(): + """ + Detect if the host system is 64 bit. + :return: True if system is 64 bit. + """ + 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 + """ + 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. + :return: Path to multibootusb directory of host system. + """ + if platform.system() == "Linux": + home_dir = os.path.expanduser('~') + mbusb_dir = os.path.join(home_dir, ".multibootusb") + elif platform.system() == "Windows": + mbusb_dir = os.path.join(tempfile.gettempdir(), "multibootusb") + + return mbusb_dir + + +def iso_cfg_ext_dir(): + """ + Function to return the path to ISO configuration file extraction directory. + :return: Path to directory where ISO config files will be extracted. + """ + return os.path.join(multibootusb_host_dir(), 'iso_cfg_ext_dir') + + +def clean_iso_cfg_ext_dir(iso_cfg_ext_dir): + """ + Clean old ISO config files extracted by previous use of multibootusb. + :param iso_cfg_ext_dir: Path to config extract directory. + :return: + """ + if os.path.exists(iso_cfg_ext_dir): + filelist = [f for f in os.listdir(iso_cfg_ext_dir)] + for f in filelist: + if os.path.isdir(os.path.join(iso_cfg_ext_dir, f)): + shutil.rmtree(os.path.join(iso_cfg_ext_dir, f)) + else: + os.remove(os.path.join(iso_cfg_ext_dir, f)) + else: + log('iso_cfg_ext_dir directory does not exist.') + + +def copy_mbusb_dir_usb(usb_disk): + """ + Copy the multibootusb directory to USB mount path. + :param usb_mount_path: Path to USB mount. + :return: + """ + from .iso import iso_size + from .usb import details + + usb_details = details(usb_disk) + usb_mount_path = usb_details['mount_point'] + if not os.path.exists(os.path.join(usb_mount_path, "multibootusb")): + try: + log('Copying multibootusb directory to ' + usb_mount_path) + shutil.copytree(resource_path(os.path.join("data", "multibootusb")), os.path.join(usb_mount_path, "multibootusb")) + return True + except: + return False + else: + log('multibootus directory already exist. Not copying.') + + +def read_input_yes(): + """ + List option and read user input + :return: True if user selected yes or else false + """ + yes_list = ['Y', 'y', 'Yes', 'yes', 'YES'] + no_list = ['N', 'n', 'No', 'no', 'NO'] + response = input("Please enter the option listed above : ") + if response in yes_list: + return True + elif response in no_list: + return False + + +def strings(filename, min=4): + with open(filename, errors="ignore") as f: + result = "" + for c in f.read(): + if c in string.printable: + result += c + continue + if len(result) >= min: + yield result + result = "" + if len(result) >= min: # catch result at EOF + yield result + + +def size_not_enough(iso_link, usb_disk): + from .iso import iso_size + from .usb import details + isoSize = iso_size(iso_link) + usb_details = details(usb_disk) + usb_size = usb_details['size_free'] + if isoSize > usb_size: + return True + else: + return False + + +def mbusb_version(): + version = open(resource_path(os.path.join("data", "version.txt")), 'r').read().strip() + return version + + +def prepare_mbusb_host_dir(): + """ + Prepare multibootusb host directory and extract data files for use. + :return: + """ + home = multibootusb_host_dir() + if not os.path.exists(home): + os.makedirs(home) + else: + log("Cleaning old multibootusb directory...") + shutil.rmtree(home) + os.makedirs(home) + + if not os.path.exists(os.path.join(home, "preference")): + os.makedirs(os.path.join(home, "preference")) + + if not os.path.exists(os.path.join(home, "iso_cfg_ext_dir")): + os.makedirs(os.path.join(home, "iso_cfg_ext_dir")) + + if os.path.exists(os.path.join(home, "syslinux", "bin", "syslinux4")): + log("Syslinux exist in multibootusb directory...") + else: + log("Extracting syslinux to multibootusb directory...") + if platform.system() == "Linux": + if sys_64bits() is True: + log('Host OS is 64 bit...') + log("Extracting syslinux 64 bit...") + # log(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip"))) + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip")), "r") as z: + z.extractall(home) + else: + log("Extracting syslinux 32 bit...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux.zip")), "r") as z: + z.extractall(home) + else: + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_windows.zip")), "r") as z: + z.extractall(home) + log("Extracting syslinux modules to multibootusb directory...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_modules.zip")), "r") as z: + z.extractall(os.path.join(home, "syslinux")) + + if os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + log(os.listdir(os.path.join(home, "iso_cfg_ext_dir"))) + log("iso extract directory is not empty.") + log("Removing junk files...") + for files in os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + if os.path.isdir(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))): + log (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + shutil.rmtree(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + else: + try: + log (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + os.unlink(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.remove(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + except OSError: + log("Can't remove the file. Skipping it.") + +if __name__ == '__main__': + log(quote("""Test-string""")) + log(has_digit("test-string-with-01-digit")) + log(sys_64bits()) + log(multibootusb_host_dir()) + log(iso_cfg_ext_dir()) + strings_test = strings('../../text-stings.bin') + log(strings_test) diff --git a/deb_dist/multibootusb-8.5.0/scripts/gui/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/deb_dist/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py b/deb_dist/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py new file mode 100644 index 0000000..d5c8913 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'scripts\gui\multibootusb.ui' +# +# Created by: PyQt5 UI code generator 5.6 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(644, 516) + self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog) + self.horizontalLayout.setObjectName("horizontalLayout") + self.tabWidget = QtWidgets.QTabWidget(Dialog) + self.tabWidget.setObjectName("tabWidget") + self.tab_3 = QtWidgets.QWidget() + self.tab_3.setObjectName("tab_3") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab_3) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_persistence_value = QtWidgets.QLabel(self.tab_3) + self.label_persistence_value.setObjectName("label_persistence_value") + self.gridLayout.addWidget(self.label_persistence_value, 5, 2, 1, 1) + self.verticalLayout_11 = QtWidgets.QVBoxLayout() + self.verticalLayout_11.setObjectName("verticalLayout_11") + self.groupBox_11 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_11.setObjectName("groupBox_11") + self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.groupBox_11) + self.horizontalLayout_8.setObjectName("horizontalLayout_8") + self.detect_usb = QtWidgets.QPushButton(self.groupBox_11) + self.detect_usb.setObjectName("detect_usb") + self.horizontalLayout_8.addWidget(self.detect_usb) + self.verticalLayout_11.addWidget(self.groupBox_11) + self.gridLayout.addLayout(self.verticalLayout_11, 1, 3, 1, 2) + self.listWidget = QtWidgets.QListWidget(self.tab_3) + self.listWidget.setObjectName("listWidget") + self.gridLayout.addWidget(self.listWidget, 0, 0, 4, 3) + self.progressBar = QtWidgets.QProgressBar(self.tab_3) + self.progressBar.setProperty("value", 0) + self.progressBar.setObjectName("progressBar") + self.gridLayout.addWidget(self.progressBar, 7, 0, 1, 6) + self.create = QtWidgets.QPushButton(self.tab_3) + self.create.setObjectName("create") + self.gridLayout.addWidget(self.create, 5, 4, 1, 1) + self.comboBox = QtWidgets.QComboBox(self.tab_3) + self.comboBox.setObjectName("comboBox") + self.gridLayout.addWidget(self.comboBox, 0, 3, 1, 1) + self.labelstep1 = QtWidgets.QLabel(self.tab_3) + self.labelstep1.setObjectName("labelstep1") + self.gridLayout.addWidget(self.labelstep1, 0, 5, 1, 1) + self.labelstep2 = QtWidgets.QLabel(self.tab_3) + self.labelstep2.setObjectName("labelstep2") + self.gridLayout.addWidget(self.labelstep2, 4, 5, 1, 1) + self.close = QtWidgets.QPushButton(self.tab_3) + self.close.setObjectName("close") + self.gridLayout.addWidget(self.close, 5, 3, 1, 1) + self.labelstep3 = QtWidgets.QLabel(self.tab_3) + self.labelstep3.setObjectName("labelstep3") + self.gridLayout.addWidget(self.labelstep3, 5, 5, 1, 1) + self.slider_persistence = QtWidgets.QSlider(self.tab_3) + self.slider_persistence.setEnabled(False) + self.slider_persistence.setAutoFillBackground(False) + self.slider_persistence.setOrientation(QtCore.Qt.Horizontal) + self.slider_persistence.setTickPosition(QtWidgets.QSlider.TicksBothSides) + self.slider_persistence.setTickInterval(0) + self.slider_persistence.setObjectName("slider_persistence") + self.gridLayout.addWidget(self.slider_persistence, 5, 1, 1, 1) + self.groupBox = QtWidgets.QGroupBox(self.tab_3) + self.groupBox.setObjectName("groupBox") + self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout_5 = QtWidgets.QGridLayout() + self.gridLayout_5.setObjectName("gridLayout_5") + self.uninstall = QtWidgets.QPushButton(self.groupBox) + self.uninstall.setObjectName("uninstall") + self.gridLayout_5.addWidget(self.uninstall, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_5) + self.gridLayout.addWidget(self.groupBox, 3, 3, 1, 2) + self.lineEdit = QtWidgets.QLineEdit(self.tab_3) + self.lineEdit.setObjectName("lineEdit") + self.gridLayout.addWidget(self.lineEdit, 4, 0, 1, 4) + self.groupBox_6 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_6.setObjectName("groupBox_6") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_6) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.usb_dev = QtWidgets.QLabel(self.groupBox_6) + self.usb_dev.setObjectName("usb_dev") + self.verticalLayout_5.addWidget(self.usb_dev) + self.usb_vendor = QtWidgets.QLabel(self.groupBox_6) + self.usb_vendor.setObjectName("usb_vendor") + self.verticalLayout_5.addWidget(self.usb_vendor) + self.usb_model = QtWidgets.QLabel(self.groupBox_6) + self.usb_model.setObjectName("usb_model") + self.verticalLayout_5.addWidget(self.usb_model) + self.usb_size = QtWidgets.QLabel(self.groupBox_6) + self.usb_size.setObjectName("usb_size") + self.verticalLayout_5.addWidget(self.usb_size) + self.usb_mount = QtWidgets.QLabel(self.groupBox_6) + self.usb_mount.setObjectName("usb_mount") + self.verticalLayout_5.addWidget(self.usb_mount) + self.gridLayout.addWidget(self.groupBox_6, 2, 3, 1, 3) + self.browse_iso = QtWidgets.QPushButton(self.tab_3) + self.browse_iso.setObjectName("browse_iso") + self.gridLayout.addWidget(self.browse_iso, 4, 4, 1, 1) + self.label_persistence = QtWidgets.QLabel(self.tab_3) + self.label_persistence.setObjectName("label_persistence") + self.gridLayout.addWidget(self.label_persistence, 5, 0, 1, 1) + self.checkBox_all_drives = QtWidgets.QCheckBox(self.tab_3) + self.checkBox_all_drives.setObjectName("checkBox_all_drives") + self.gridLayout.addWidget(self.checkBox_all_drives, 0, 4, 1, 1) + self.status = QtWidgets.QLabel(self.tab_3) + self.status.setMinimumSize(QtCore.QSize(0, 0)) + self.status.setAcceptDrops(False) + self.status.setAutoFillBackground(False) + self.status.setFrameShadow(QtWidgets.QFrame.Plain) + self.status.setText("") + self.status.setTextFormat(QtCore.Qt.AutoText) + self.status.setScaledContents(False) + self.status.setObjectName("status") + self.gridLayout.addWidget(self.status, 6, 0, 1, 6) + self.horizontalLayout_2.addLayout(self.gridLayout) + self.tabWidget.addTab(self.tab_3, "") + self.imager = QtWidgets.QWidget() + self.imager.setObjectName("imager") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.imager) + self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.gridLayout_9 = QtWidgets.QGridLayout() + self.gridLayout_9.setObjectName("gridLayout_9") + self.groupBox_7 = QtWidgets.QGroupBox(self.imager) + self.groupBox_7.setObjectName("groupBox_7") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_7) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.gridLayout_11 = QtWidgets.QGridLayout() + self.gridLayout_11.setObjectName("gridLayout_11") + self.groupBox_9 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_9.setObjectName("groupBox_9") + self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.groupBox_9) + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.comboBox_2 = QtWidgets.QComboBox(self.groupBox_9) + self.comboBox_2.setObjectName("comboBox_2") + self.verticalLayout_8.addWidget(self.comboBox_2) + self.pushbtn_imager_refreshusb = QtWidgets.QPushButton(self.groupBox_9) + self.pushbtn_imager_refreshusb.setObjectName("pushbtn_imager_refreshusb") + self.verticalLayout_8.addWidget(self.pushbtn_imager_refreshusb) + self.imager_disk_label = QtWidgets.QLabel(self.groupBox_9) + self.imager_disk_label.setObjectName("imager_disk_label") + self.verticalLayout_8.addWidget(self.imager_disk_label) + self.imager_total_size = QtWidgets.QLabel(self.groupBox_9) + self.imager_total_size.setObjectName("imager_total_size") + self.verticalLayout_8.addWidget(self.imager_total_size) + self.imager_uuid = QtWidgets.QLabel(self.groupBox_9) + self.imager_uuid.setObjectName("imager_uuid") + self.verticalLayout_8.addWidget(self.imager_uuid) + self.gridLayout_11.addWidget(self.groupBox_9, 0, 0, 1, 1) + self.groupBox_10 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_10.setObjectName("groupBox_10") + self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.groupBox_10) + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.pushButton = QtWidgets.QPushButton(self.groupBox_10) + self.pushButton.setObjectName("pushButton") + self.verticalLayout_9.addWidget(self.pushButton) + self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_10) + self.lineEdit_3.setObjectName("lineEdit_3") + self.verticalLayout_9.addWidget(self.lineEdit_3) + self.imager_bootable = QtWidgets.QLabel(self.groupBox_10) + self.imager_bootable.setObjectName("imager_bootable") + self.verticalLayout_9.addWidget(self.imager_bootable) + self.imager_iso_size = QtWidgets.QLabel(self.groupBox_10) + self.imager_iso_size.setObjectName("imager_iso_size") + self.verticalLayout_9.addWidget(self.imager_iso_size) + self.gridLayout_11.addWidget(self.groupBox_10, 0, 1, 1, 1) + self.verticalLayout_6.addLayout(self.gridLayout_11) + self.gridLayout_9.addWidget(self.groupBox_7, 1, 0, 1, 1) + self.groupBox_8 = QtWidgets.QGroupBox(self.imager) + self.groupBox_8.setObjectName("groupBox_8") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_8) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.imager_label_status = QtWidgets.QLabel(self.groupBox_8) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.imager_label_status.sizePolicy().hasHeightForWidth()) + self.imager_label_status.setSizePolicy(sizePolicy) + self.imager_label_status.setText("") + self.imager_label_status.setObjectName("imager_label_status") + self.verticalLayout_7.addWidget(self.imager_label_status) + self.gridLayout_12 = QtWidgets.QGridLayout() + self.gridLayout_12.setObjectName("gridLayout_12") + self.imager_write = QtWidgets.QPushButton(self.groupBox_8) + self.imager_write.setObjectName("imager_write") + self.gridLayout_12.addWidget(self.imager_write, 2, 2, 1, 1) + self.imager_close = QtWidgets.QPushButton(self.groupBox_8) + self.imager_close.setObjectName("imager_close") + self.gridLayout_12.addWidget(self.imager_close, 2, 1, 1, 1) + self.label_10 = QtWidgets.QLabel(self.groupBox_8) + self.label_10.setObjectName("label_10") + self.gridLayout_12.addWidget(self.label_10, 1, 0, 1, 3) + self.imager_progressbar = QtWidgets.QProgressBar(self.groupBox_8) + self.imager_progressbar.setProperty("value", 0) + self.imager_progressbar.setObjectName("imager_progressbar") + self.gridLayout_12.addWidget(self.imager_progressbar, 0, 0, 1, 3) + self.verticalLayout_7.addLayout(self.gridLayout_12) + self.gridLayout_9.addWidget(self.groupBox_8, 2, 0, 1, 1) + self.horizontalLayout_7.addLayout(self.gridLayout_9) + self.tabWidget.addTab(self.imager, "") + self.syslinux_ab = QtWidgets.QWidget() + self.syslinux_ab.setObjectName("syslinux_ab") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.syslinux_ab) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.gridLayout_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.groupBox_2 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_2.setAutoFillBackground(False) + self.groupBox_2.setObjectName("groupBox_2") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_2) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.gridLayout_3 = QtWidgets.QGridLayout() + self.gridLayout_3.setObjectName("gridLayout_3") + self.install_syslinux = QtWidgets.QPushButton(self.groupBox_2) + self.install_syslinux.setObjectName("install_syslinux") + self.gridLayout_3.addWidget(self.install_syslinux, 2, 1, 1, 1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem, 2, 0, 1, 1) + self.install_sys_all = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_all.setObjectName("install_sys_all") + self.gridLayout_3.addWidget(self.install_sys_all, 1, 0, 1, 2) + self.install_sys_only = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_only.setObjectName("install_sys_only") + self.gridLayout_3.addWidget(self.install_sys_only, 0, 0, 1, 2) + self.horizontalLayout_4.addLayout(self.gridLayout_3) + self.gridLayout_2.addWidget(self.groupBox_2, 0, 0, 1, 1) + self.groupBox_3 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_3.setObjectName("groupBox_3") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_3) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.gridLayout_4 = QtWidgets.QGridLayout() + self.gridLayout_4.setObjectName("gridLayout_4") + self.edit_syslinux = QtWidgets.QPushButton(self.groupBox_3) + self.edit_syslinux.setObjectName("edit_syslinux") + self.gridLayout_4.addWidget(self.edit_syslinux, 1, 1, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_4.addItem(spacerItem1, 1, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(self.groupBox_3) + self.label_2.setObjectName("label_2") + self.gridLayout_4.addWidget(self.label_2, 0, 0, 1, 2) + self.horizontalLayout_5.addLayout(self.gridLayout_4) + self.gridLayout_2.addWidget(self.groupBox_3, 1, 0, 1, 1) + self.horizontalLayout_3.addLayout(self.gridLayout_2) + self.tabWidget.addTab(self.syslinux_ab, "") + self.tab = QtWidgets.QWidget() + self.tab.setObjectName("tab") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.groupBox_5 = QtWidgets.QGroupBox(self.tab) + self.groupBox_5.setObjectName("groupBox_5") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_5) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.gridLayout_7 = QtWidgets.QGridLayout() + self.gridLayout_7.setObjectName("gridLayout_7") + self.ram_iso_2048 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_2048.setObjectName("ram_iso_2048") + self.gridLayout_7.addWidget(self.ram_iso_2048, 4, 4, 1, 1) + self.ram_iso_1024 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_1024.setObjectName("ram_iso_1024") + self.gridLayout_7.addWidget(self.ram_iso_1024, 4, 3, 1, 1) + self.ram_iso_256 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_256.setObjectName("ram_iso_256") + self.gridLayout_7.addWidget(self.ram_iso_256, 4, 0, 1, 1) + self.browse_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.browse_iso_qemu.setObjectName("browse_iso_qemu") + self.gridLayout_7.addWidget(self.browse_iso_qemu, 2, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_5) + self.label_7.setObjectName("label_7") + self.gridLayout_7.addWidget(self.label_7, 0, 0, 1, 5) + self.ram_iso_512 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_512.setObjectName("ram_iso_512") + self.gridLayout_7.addWidget(self.ram_iso_512, 4, 1, 1, 1) + self.boot_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.boot_iso_qemu.setObjectName("boot_iso_qemu") + self.gridLayout_7.addWidget(self.boot_iso_qemu, 6, 4, 1, 1) + self.ram_iso_768 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_768.setObjectName("ram_iso_768") + self.gridLayout_7.addWidget(self.ram_iso_768, 4, 2, 1, 1) + self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_2.setObjectName("lineEdit_2") + self.gridLayout_7.addWidget(self.lineEdit_2, 2, 0, 1, 4) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem2, 3, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.groupBox_5) + self.label_3.setObjectName("label_3") + self.gridLayout_7.addWidget(self.label_3, 6, 0, 1, 4) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem3, 5, 0, 1, 1) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem4, 1, 0, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout_7) + self.verticalLayout_2.addWidget(self.groupBox_5) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem5) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.groupBox_4 = QtWidgets.QGroupBox(self.tab) + self.groupBox_4.setObjectName("groupBox_4") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_4) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.gridLayout_8 = QtWidgets.QGridLayout() + self.gridLayout_8.setObjectName("gridLayout_8") + self.ram_usb_768 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_768.setObjectName("ram_usb_768") + self.gridLayout_8.addWidget(self.ram_usb_768, 2, 2, 1, 1) + self.ram_usb_256 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_256.setObjectName("ram_usb_256") + self.gridLayout_8.addWidget(self.ram_usb_256, 2, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(self.groupBox_4) + self.label_6.setObjectName("label_6") + self.gridLayout_8.addWidget(self.label_6, 0, 0, 1, 5) + self.ram_usb_1024 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_1024.setObjectName("ram_usb_1024") + self.gridLayout_8.addWidget(self.ram_usb_1024, 2, 3, 1, 1) + self.label_4 = QtWidgets.QLabel(self.groupBox_4) + self.label_4.setObjectName("label_4") + self.gridLayout_8.addWidget(self.label_4, 4, 0, 1, 4) + self.ram_usb_512 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_512.setObjectName("ram_usb_512") + self.gridLayout_8.addWidget(self.ram_usb_512, 2, 1, 1, 1) + self.boot_usb_qemu = QtWidgets.QPushButton(self.groupBox_4) + self.boot_usb_qemu.setObjectName("boot_usb_qemu") + self.gridLayout_8.addWidget(self.boot_usb_qemu, 4, 4, 1, 1) + self.ram_usb_2048 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_2048.setObjectName("ram_usb_2048") + self.gridLayout_8.addWidget(self.ram_usb_2048, 2, 4, 1, 1) + spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem6, 1, 1, 1, 1) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem7, 3, 2, 1, 1) + spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem8, 5, 0, 1, 1) + self.verticalLayout_4.addLayout(self.gridLayout_8) + self.gridLayout_6.addWidget(self.groupBox_4, 0, 0, 1, 1) + self.verticalLayout_2.addLayout(self.gridLayout_6) + self.tabWidget.addTab(self.tab, "") + self.tab_2 = QtWidgets.QWidget() + self.tab_2.setObjectName("tab_2") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_2) + self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.gridLayout_10 = QtWidgets.QGridLayout() + self.gridLayout_10.setObjectName("gridLayout_10") + spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem9, 0, 1, 1, 1) + spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem10, 1, 0, 1, 1) + spacerItem11 = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem11, 2, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(self.tab_2) + self.label_5.setObjectName("label_5") + self.gridLayout_10.addWidget(self.label_5, 1, 1, 1, 1) + spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem12, 1, 2, 1, 1) + self.horizontalLayout_6.addLayout(self.gridLayout_10) + self.tabWidget.addTab(self.tab_2, "") + self.horizontalLayout.addWidget(self.tabWidget) + + self.retranslateUi(Dialog) + self.tabWidget.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "multibootusb")) + self.label_persistence_value.setText(_translate("Dialog", "0 MB")) + self.groupBox_11.setTitle(_translate("Dialog", "Detect")) + self.detect_usb.setText(_translate("Dialog", "Detect Drives")) + self.create.setText(_translate("Dialog", "Create")) + self.labelstep1.setText(_translate("Dialog", "

Step 1

")) + self.labelstep2.setText(_translate("Dialog", "

Step 2

")) + self.close.setText(_translate("Dialog", "Close")) + self.labelstep3.setText(_translate("Dialog", "

Step 3

")) + self.slider_persistence.setToolTip(_translate("Dialog", "Choose Persistence size. Not all distros supports persistence...")) + self.groupBox.setTitle(_translate("Dialog", "Uninstall (Optional)")) + self.uninstall.setText(_translate("Dialog", "Uninstall Distro")) + self.groupBox_6.setTitle(_translate("Dialog", "USB Details")) + self.usb_dev.setText(_translate("Dialog", "Drive ::")) + self.usb_vendor.setText(_translate("Dialog", "Vendor ::")) + self.usb_model.setText(_translate("Dialog", "Model::")) + self.usb_size.setText(_translate("Dialog", "Size ::")) + self.usb_mount.setText(_translate("Dialog", "Mount ::")) + self.browse_iso.setText(_translate("Dialog", "Browse ISO")) + self.label_persistence.setText(_translate("Dialog", "

Persistence

")) + self.checkBox_all_drives.setText(_translate("Dialog", "All Drives")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Dialog", "MultiBootUSB")) + self.groupBox_7.setTitle(_translate("Dialog", "Imager")) + self.groupBox_9.setTitle(_translate("Dialog", "-------------- USB details -------------------")) + self.pushbtn_imager_refreshusb.setText(_translate("Dialog", "Refresh USB")) + self.imager_disk_label.setText(_translate("Dialog", "Disk Label ::")) + self.imager_total_size.setText(_translate("Dialog", "Disk Size")) + self.imager_uuid.setText(_translate("Dialog", "Disk Label ::")) + self.groupBox_10.setTitle(_translate("Dialog", "------------------------------ ISO details ----------------------------------")) + self.pushButton.setText(_translate("Dialog", "Browse ISO")) + self.imager_bootable.setText(_translate("Dialog", "Bootable ISO")) + self.imager_iso_size.setText(_translate("Dialog", "ISO Size")) + self.groupBox_8.setTitle(_translate("Dialog", "Imager Progress")) + self.imager_write.setText(_translate("Dialog", "Write/Create")) + self.imager_close.setText(_translate("Dialog", "Close")) + self.label_10.setText(_translate("Dialog", "

WARNING : Any bootable USB made using ISO Imager will destroy all data on the selected USB disk.

Use it at your own risk. Developers are not responsile for loss of any data.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.imager), _translate("Dialog", "ISO Imager")) + self.groupBox_2.setTitle(_translate("Dialog", "Install Syslinux")) + self.install_syslinux.setText(_translate("Dialog", "Install")) + self.install_sys_all.setText(_translate("Dialog", "Install syslinux and copy all required files.")) + self.install_sys_only.setText(_translate("Dialog", "Install only syslinux (existing configurations will not be altred).")) + self.groupBox_3.setTitle(_translate("Dialog", "Edit syslinux.cfg")) + self.edit_syslinux.setText(_translate("Dialog", "Edit")) + self.label_2.setText(_translate("Dialog", "

Using this option user can edit syslinux.cfg file directly. It directly uses

default editor of host system. Be careful while editing syslinux.cfg file.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.syslinux_ab), _translate("Dialog", "Syslinux")) + self.groupBox_5.setTitle(_translate("Dialog", "Boot ISO")) + self.ram_iso_2048.setText(_translate("Dialog", "2048 MB")) + self.ram_iso_1024.setText(_translate("Dialog", "1024 MB")) + self.ram_iso_256.setText(_translate("Dialog", "256 MB")) + self.browse_iso_qemu.setText(_translate("Dialog", "Browse ISO")) + self.label_7.setText(_translate("Dialog", "

Best way to test your downloaded ISOs.

")) + self.ram_iso_512.setText(_translate("Dialog", "512 MB")) + self.boot_iso_qemu.setText(_translate("Dialog", "Boot ISO")) + self.ram_iso_768.setText(_translate("Dialog", "768 MB")) + self.label_3.setText(_translate("Dialog", "Choose desired RAM and click on Boot ISO button.")) + self.groupBox_4.setTitle(_translate("Dialog", "Boot USB")) + self.ram_usb_768.setText(_translate("Dialog", "768 MB")) + self.ram_usb_256.setText(_translate("Dialog", "256 MB")) + self.label_6.setText(_translate("Dialog", "

Use this option if you want to check USB installation without reboot.

")) + self.ram_usb_1024.setText(_translate("Dialog", "1024 MB")) + self.label_4.setText(_translate("Dialog", "Choose desired RAM and click on Boot USB button.")) + self.ram_usb_512.setText(_translate("Dialog", "512 MB")) + self.boot_usb_qemu.setText(_translate("Dialog", "Boot USB")) + self.ram_usb_2048.setText(_translate("Dialog", "2048 MB")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "QEMU")) + self.label_5.setText(_translate("Dialog", "

An advanced bootable usb creator with option to install/uninstall

multiple distros. This software is written in python and pyqt.

Copyright 2010-2016 Sundar

Author(s): Sundar, Ian Bruce, Lee

Licence: GPL version 2 or later

Home page: http://multibootusb.org

Help/Email: feedback.multibootusb@gmail.com

Source Code:https://github.com/mbusb/multibootusb


")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "About")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Dialog = QtWidgets.QDialog() + ui = Ui_Dialog() + ui.setupUi(Dialog) + Dialog.show() + sys.exit(app.exec_()) + diff --git a/deb_dist/multibootusb-8.5.0/scripts/imager.py b/deb_dist/multibootusb-8.5.0/scripts/imager.py new file mode 100644 index 0000000..0d3e006 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/imager.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: imager.py +# Purpose: Module to write ISO image to selected USB disk. Uses dd as backend. +# 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 +# WARNING : Any boot-able USB made using this module will destroy data stored on USB disk. + +import os +import subprocess +import collections +import platform +import signal +from PyQt5 import QtGui +from PyQt5 import QtWidgets +from PyQt5 import QtCore +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * +from . import iso +from . import usb +from . import config +from . import progressbar + +if platform.system() == "Windows": + import win32com.client + + +def dd_linux(): + import time + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link)) + output = "of=" + config.imager_usb_disk + os.system("umount " + config.imager_usb_disk + "1") + command = ['dd', input, output, "bs=1M"] + log("Executing ==> " + " ".join(command)) + dd_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while dd_process.poll() is None: + time.sleep(.1) # If this time delay is not given, the Popen does not execute the actual command + dd_process.send_signal(signal.SIGUSR1) + dd_process.stderr.flush() + while True: + out_error = dd_process.stderr.readline().decode() + if out_error: + if 'bytes' in out_error: + copied = int(out_error.split(' ', 1)[0]) + config.imager_percentage = round((float(copied) / float(in_file_size) * 100)) + pbar.update(config.imager_percentage) + break + + if dd_process.poll() is not None: + log("Executing ==> sync") + os.system("sync") + log("ISO has been written to USB disk...") + return + + +def dd_win(): + + windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe")) + if os.path.exists(resource_path(os.path.join("data", "tools", "dd", "dd.exe"))): + log("dd exist") + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link) / 1024 / 1024) + output = "of=\\\.\\" + config.imager_usb_disk + command = [windd, input, output, "bs=1M", "--progress"] + log("Executing ==> " + " ".join(command)) + dd_process = subprocess.Popen(command, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, + shell=False) + while dd_process.poll() is None: + for line in iter(dd_process.stderr.readline, ''): + line = line.strip() + if 'error' in line.lower() or 'invalid' in line.lower(): + log("Error writing to disk...") + break + if line and line[-1] == 'M': + copied = float(line.strip('M').replace(',', '')) + config.imager_percentage = round((copied / float(in_file_size) * 100)) + + log("ISO has been written to USB disk...") + + return + + +class Imager(QtWidgets.QDialog, Ui_Dialog): + """ + Raw write to USB disk using dd. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + def on_Imager_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_3.clear() + config.imager_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + if config.imager_iso_link: + if platform.system() == "Windows": + if "/" in config.imager_iso_link: + config.imager_iso_link = config.imager_iso_link.strip().replace("/", "\\") + self.ui.lineEdit_3.insert(str(config.imager_iso_link)) + self.add_iso_gui_label_text() + else: + log("File not selected...") + + def add_iso_gui_label_text(self): + """ + Simple function to add text label to GUI widgets. + :return: + """ + log("Testing ISO...") + if iso.is_bootable(config.imager_iso_link) is True: + self.ui.imager_bootable.setText("Bootable ISO :: Yes") + log("ISO is bootable.") + else: + self.ui.imager_bootable.setText("Bootable ISO :: No") + log("ISO is not bootable.") + + if os.path.exists(config.imager_iso_link): + log("Path " + config.imager_iso_link + " is exist...") + self.iso_size = str(round(os.path.getsize(config.imager_iso_link) / 1024 / 1024)) + self.ui.imager_iso_size.setText("ISO Size :: " + self.iso_size + " MB") + log("ISO Size is " + self.iso_size + " MB") + + def onImagerComboChange(self): + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.imager_usb_disk): + self.ui.imager_disk_label.setText("Disk Type :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).usb_type) + self.ui.imager_total_size.setText("Disk Size :: " + usb.bytes2human(int(self.imager_usb_detail + (config.imager_usb_disk, + partition=0).total_size))) + if platform.system() == "Linux": + self.ui.imager_uuid.setText("Disk Model :: " + str(self.imager_usb_detail(config.imager_usb_disk, + partition=0).model)) + else: + self.ui.imager_uuid.setText("Disk Label :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).model) + + def imager_list_usb(self, partition=1): + """ + Function to detect whole USB disk. It uses lsblk package on Linux. + :param partition: What to return. By default partition is set. + :return: USB disk/partition as list + """ + disk = [] + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -i", shell=True) + if not partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and (line[5].strip()) == b'disk': + disk.append(str("/dev/" + str(line[0].strip().decode()))) + elif partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and line[5].strip() == b'part': + disk.append(str("/dev/" + str(line[0].strip()[2:]))) + else: + if partition == 1 or not partition == 1: + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + disk.append(drive) + return disk + + def imager_usb_detail(self, usb_disk, partition=1): + """ + Function to detect details of USB disk using lsblk + :param usb_disk: path to usb disk + :param partition: by default partition is set (but yet to code for it) + :return: details of size, type and model as tuples + """ + _ntuple_diskusage = collections.namedtuple('usage', 'total_size usb_type model') + + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -ib " + usb_disk, shell=True) + for line in output.splitlines(): + line = line.split() + if not partition == 1: + if line[2].strip() == b'1' and line[5].strip() == b'disk': + total_size = line[3] + if not total_size: + total_size = "Unknown" + usb_type = "Removable" + model = subprocess.check_output("lsblk -in -f -o MODEL " + usb_disk, shell=True).decode() + if not model: + model = "Unknown" + else: + try: + selected_usb_part = str(usb_disk) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No label." + total_size = d.TotalSize + usb_type = "Removable" + model = label + except: + log("Error detecting USB details.") + + return _ntuple_diskusage(total_size, usb_type, model) + + def get_usb_size(self, usb_disk): + """ + Function to detect USB disk space. Useful but not used in multibootusb as of now. + :param usb_disk: USB disk like "/dev/sdb" + :return: Size of the disk as integer + """ + if platform.system() == "Linux": + cat_output = subprocess.check_output("cat /proc/partitions | grep " + usb_disk[5:], shell=True) + usb_size = int(cat_output.split()[2]) * 1024 + # log(usb_size) + return usb_size + else: + usb_size = self.usb.disk_usage(self.usb.get_usb(usb_disk).mount).total + return usb_size diff --git a/deb_dist/multibootusb-8.5.0/scripts/install.py b/deb_dist/multibootusb-8.5.0/scripts/install.py new file mode 100644 index 0000000..7c34d8b --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/install.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: install.py +# Purpose: This module contain functions to install ISO files to USB disk non destructively. +# 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 shutil +import sys +import platform +import threading +import subprocess +from .usb import * +from .gen import * +# from .iso import * +from . import iso +from scripts.update_cfg_file import * +from . import config +from . import persistence + + +def install_distro(): + """ + Install selected ISO to USB disk. + :return: + """ + usb_mount = config.usb_mount + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(config.iso_link)) + _iso_file_list = iso.iso_file_list(config.iso_link) + + if not os.path.exists(os.path.join(usb_mount, "multibootusb")): + log("Copying multibootusb directory to " + usb_mount) + shutil.copytree(resource_path(os.path.join("data", "tools", "multibootusb")), + os.path.join(config.usb_mount, "multibootusb")) + + if not os.path.exists(install_dir): + os.makedirs(install_dir) + with open(os.path.join(install_dir, "multibootusb.cfg"), "w") as f: + f.write(config.distro) + with open(os.path.join(install_dir, "iso_file_list.cfg"), 'w') as f: + for file_path in _iso_file_list: + f.write(file_path + "\n") + log("Installing " + iso_name(config.iso_link) + " on " + install_dir) + + if config.distro == "opensuse": + iso.iso_extract_file(config.iso_link, install_dir, 'boot') + status_text = "Copying ISO..." + if platform.system() == "Windows": + subprocess.call(["xcopy", config.iso_link, usb_mount], shell=True) # Have to use xcopy as python file copy is dead slow. + elif platform.system() == "Linux": + log("Copying " + config.iso_link + " to " + usb_mount) + shutil.copy(config.iso_link, usb_mount) + elif config.distro == "Windows" or config.distro == "alpine" or config.distro == 'pc-unlocker': + log("Extracting iso to " + usb_mount) + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == "trinity-rescue": + iso.iso_extract_file(config.iso_link, usb_mount, '*trk3') + elif config.distro == "ipfire": + iso.iso_extract_file(config.iso_link, usb_mount, '*.tlz') + iso.iso_extract_file(config.iso_link, usb_mount, 'distro.img') + iso.iso_extract_file(config.iso_link, install_dir, 'boot') + elif config.distro == "zenwalk": + config.status_text = "Copying ISO..." + iso.iso_extract_file(config.iso_link, install_dir, "kernel") + copy_iso(config.iso_link, install_dir) + elif config.distro == "salix-live": + # iso.iso_extract_file(config.iso_link, install_dir, "boot") + iso.iso_extract_file(config.iso_link, install_dir, '*syslinux') + iso.iso_extract_file(config.iso_link, install_dir, '*menus') + iso.iso_extract_file(config.iso_link, install_dir, '*vmlinuz') + iso.iso_extract_file(config.iso_link, install_dir, '*initrd*') + iso.iso_extract_file(config.iso_link, usb_mount, '*modules') + iso.iso_extract_file(config.iso_link, usb_mount, '*packages') + iso.iso_extract_file(config.iso_link, usb_mount, '*optional') + iso.iso_extract_file(config.iso_link, usb_mount, '*liveboot') + #iso.iso_extract_full(config.iso_link, usb_mount) + # config.status_text = "Copying ISO..." + # copy_iso(config.iso_link, install_dir) + elif config.distro == 'sgrubd2': + copy_iso(config.iso_link, install_dir) + elif config.distro == 'alt-linux': + iso.iso_extract_file(config.iso_link, install_dir, '-xr!*rescue') + iso.iso_extract_file(config.iso_link, config.usb_mount, 'rescue') + elif config.distro == "generic": + #with open(os.path.join(install_dir, "generic.cfg"), "w") as f: + # f.write(os.path.join(isolinux_bin_dir(config.iso_link), "generic") + ".bs") + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'grub4dos': + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'ReactOS': + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'grub4dos_iso': + copy_iso(config.iso_link, install_dir) + else: + iso.iso_extract_full(config.iso_link, install_dir) + + if platform.system() == 'Linux': + log('ISO extracted successfully. Sync is in progress...') + os.system('sync') + + if config.persistence != 0: + log('Creating Persistence...') + config.status_text = 'Creating Persistence...' + persistence.create_persistence() + + install_patch() + + +def copy_iso(src, dst): + """ + A simple wrapper for copying larger files. This is necessary as + shutil copy files is much slower under Windows platform + :param src: Path to source file + :param dst: Destination directory + :return: + """ + if platform.system() == "Windows": + subprocess.call("xcopy " + src + " " + dst, shell=True) + elif platform.system() == "Linux": + shutil.copy(src, dst) + +def install_progress(): + """ + Function to calculate progress percentage of install. + :return: + """ + from . import progressbar + + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + usb_size_used = usb_details['size_used'] + thrd = threading.Thread(target=install_distro, name="install_progress") + # thrd.daemon() + # install_size = usb_size_used / 1024 + install_size = iso_size(config.iso_link) / 1024 + final_size = (usb_size_used + iso_size(config.iso_link)) + config.persistence + thrd.start() + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = details(config.usb_disk)['size_used'] + percentage = int((current_size / final_size) * 100) + if percentage > 100: + percentage = 100 + config.percentage = percentage + pbar.update(percentage) + + +def install_patch(): + """ + Function to certain distros which uses makeboot.sh script for making bootable usb disk. + This is required to make sure that same version (32/64 bit) of modules present is the isolinux directory + :return: + """ + if config.distro == 'debian': + if platform.system() == 'Linux': # Need to syn under Linux. Otherwise, USB disk becomes random read only. + os.system('sync') + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(config.iso_link)) + iso_linux_bin_dir = isolinux_bin_dir(config.iso_link) + config.syslinux_version = isolinux_version(isolinux_path) + iso_file_list = iso.iso_file_list(config.iso_link) + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)) + if any("makeboot.sh" in s.lower() for s in iso_file_list): + for module in os.listdir(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link))): + if module.endswith(".c32"): + if os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module)): + try: + os.remove(os.path.join(config.usb_mount, "multibootusb", + iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link), module)) + log("Copying " + module) + log((resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module))) + shutil.copy(resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module)) + except Exception as err: + log(err) + log("Could not copy " + module) + else: + log('Patch not required...') + + +if __name__ == '__main__': + config.iso_link = '../../../DISTROS/2016/slitaz-4.0.iso' + install_distro() diff --git a/deb_dist/multibootusb-8.5.0/scripts/iso.py b/deb_dist/multibootusb-8.5.0/scripts/iso.py new file mode 100644 index 0000000..805e9e3 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/iso.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: iso.py +# Purpose: Module to manupulate ISO file +# Authors: Sundar +# Depends: isodump3.py (Authored by Johni Lee for MultiBootUSB) +# 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 sys +import os +import string +import platform +import re +from .gen import * +from .isodump3 import ISO9660 +from . import _7zip + + +_iso_cfg_ext_dir = iso_cfg_ext_dir() + + +def iso_name(iso_link): + """ + Find the name of an ISO. + :return: Name of an ISO (with extension) as string. Returns If not returns None. + """ + if os.path.exists(iso_link): + try: + name = os.path.basename(str(iso_link)) + except: + name = None + else: + name = None + + return name + + +def iso_basename(iso_link): + """ + Find the base name of an ISO. + :return: Base name (without extension) of a selected ISO as string. If not returns None. + """ + try: + dir_name = str(os.path.splitext(os.path.basename(str(iso_link)))[0]) + except: + dir_name = None + + return dir_name + + +def isolinux_bin_exist(iso_link): + """ + Check if an "isolinux.bin" file exist. + :return: True if "isolinux.bin" file exist of False if not. + """ + if os.path.exists(iso_link): + iso_file_list = _7zip.list_iso(iso_link) + if any("isolinux.bin" in s.lower() for s in iso_file_list): + return True + else: + return False + + + +def iso_size(iso_link): + return os.path.getsize(iso_link) + + +def is_bootable(iso_link): + """ + Check if an ISO has the ability to boot. + :return: True if ISO is bootable and False if not. + """ + iso9660fs = ISO9660(iso_link) + isBootable = iso9660fs.checkISOBootable() + if isBootable: + return True + else: + return False + + +def isolinux_bin_dir(iso_link): + """ + Detects "isolinux.bin" directory. + :return: path of "isolinux.bin" directory as string. + """ + if os.path.exists(iso_link): + bin_dir = False + iso_file_list = _7zip.list_iso(iso_link) + if any("isolinux.bin" in s.lower() for s in iso_file_list): + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + if 'efi' not in f.lower(): # Certain distros place their isolinux.bin in to /EFI/BOOT director and we don't want to include them + bin_dir = os.path.dirname(f) + break + + return bin_dir + + +def isolinux_bin_path(iso_link): + """ + Detects pat to "isolinux.bin". + :return: path of "isolinux.bin" as a string. + """ + iso_bin_path = False + if isolinux_bin_exist(iso_link) is not False: + iso_file_list = _7zip.list_iso(iso_link) + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + iso_bin_path = f + break + + return iso_bin_path + + +def iso_menu_lst_path(iso_link): + """ + Detects pat to "menu.lst" of grub4dos. + :return: path of "menu.lst" as a string. + """ + menu_lst_path = False + iso_file_list = _7zip.list_iso(iso_link) + for f in iso_file_list: + if 'menu.lst' in f.lower(): + menu_lst_path = f + break + + return menu_lst_path + + +def integrity(iso_link): + """ + Check the integrity of an ISO. + :return: True if integrity passes or False if it fails. + """ + return _7zip.test_iso(iso_link) + + +def iso_file_list(iso_link): + """ + Function to return the content of an ISO. + :return: List of files of an ISO as list. + """ + return _7zip.list_iso(iso_link) + + +def isolinux_version(isolinux_bin_path): + """ + Detect isolinux version shipped by distros. + :param isolinux_path: Path to "isolinux.bin" + :return: Version number as string. + """ + version = ["3", "4", "5", "6"] + if isolinux_bin_path is not None: + sl = list(strings(isolinux_bin_path)) + for strin in sl: + if re.search(r'isolinux ', strin, re.I): + for number in version: + if re.search(r'isolinux ' + number, strin, re.I): + log("\n\nFound syslinux version " + number + "\n\n") + return str(number) + + +def iso_extract_file(iso_link, dest_dir, filter): + """ + Extract the specific file(s) from an ISO + :param dest_dir: Path to destination directory. + :param filter: Filter to extract particular file(s) + :return: Extract file(s) to destination. + """ + _7zip.extract_iso(iso_link, dest_dir, filter) + + +def extract_cfg_file(iso_link): + """ + Function to extract certain files for auto detecting supported distros + :param iso_link: Path to ISO file + :return: + """ + _pattern = ['.cfg', '.CFG', '.txt', '.TXT', 'isolinux.bin', 'ISOLINUX.BIN', '.lst'] + # file_list = iso_file_list(iso_link) + for ext in _pattern: + _7zip.extract_iso(iso_link, _iso_cfg_ext_dir, pattern='*' + ext) + + +def iso_extract_full(iso_link, dest_dir): + """ + Extract an ISO to destination directory + :param dest_dir: Destination path as string. + :return: False if it fails or extract ISO files to destination directory. + """ + _7zip.extract_iso(iso_link, dest_dir) + + +if __name__ == '__main__': + #iso_path = '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso' + iso_path = '../../../DISTROS/2015/super_grub2_disk_hybrid_2.02s3.iso' + test_iso_bin_path = os.path.join('test', 'isolinux', 'isolinux.bin') + log('iso_name(iso_path) : ', iso_name(iso_path)) + log('iso_basename(iso_path) : ', iso_basename(iso_path)) + log('Integrity of ISO is : ', integrity(iso_path)) + f_list = (iso_file_list(iso_path)) + if f_list: + for f in f_list: + log(f) + log('isolinux_bin_exist(iso_path) : ', isolinux_bin_exist(iso_path)) + #log('is_bootable : ', is_bootable(iso_path)) + log('isolinux_bin_dir() : ', isolinux_bin_dir(iso_path)) + log('isolinux_bin_path(iso_path) : ', isolinux_bin_path(iso_path)) + iso_extract_full(iso_path, 'test') + iso_extract_file(iso_path, 'test', 'isolinux.bin') + log(isolinux_version(test_iso_bin_path)) + diff --git a/deb_dist/multibootusb-8.5.0/scripts/isodump3.py b/deb_dist/multibootusb-8.5.0/scripts/isodump3.py new file mode 100644 index 0000000..5e572ec --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/isodump3.py @@ -0,0 +1,806 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: isodump3.py +# Purpose: Module to list and extract iso files. +# Authors: LiQiong Lee (written exclusively for multibootusb) +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License version 3 +# Credit : I am grateful to LiQiong Lee. He not only wrote this module for multibootusb, but also extended the same +# to python3 within short time after request. + +""" ISO9660fs +Dump raw meta data of iso9660 file system. +Extract directories and files. +""" +## +## Extract directory or file from iso. +## Support RRIP. +## + +# Author : joni +# version : 1.0 + +import sys +import struct +import os +import re +import stat +from ctypes import * +from . import config +from . import gen + + +BLOCK_SIZE = 2048 +S_IFSOCKET = 0o140000 +S_IFLINK = 0o120000 +S_IFREG = 0o100000 +S_IFBLK = 0o060000 +S_IFCHR = 0o020000 +S_IFDIR = 0o040000 +S_IFIFO = 0o010000 + +E_SUCCESS = 0 +E_FAILURE = -1 +E_DEVICEFILE = -2 # can't write device file + +class PrimaryVolume(Structure): + def __init__(self): + self.sysIdentifier = "" + self.volIdentifier = "" + self.volSize = 0 + self.volSeq = 0 + self.blockSize = 0 + self.ptSize = 0 + self.ptLRd = 0 + self.fsVer = 0 + self.rootLoc = 0 + self.rootTotal = 0 + +class Rrip(Structure): + def __init__(self): + self.offset = -1 + self.altname = "" + self.devH = 0 + self.devL = 0 + self.fMode = 0 + +class DirRecord(Structure): + def __init__(self): + self.lenDr = 0 + self.lenEattr = 0 + self.locExtent= 0 + self.lenData = 0 + self.dtYear = 0 + self.dtMonth = 0 + self.dtHour = 0 + self.dtMinute = 0 + self.dtSecond = 0 + self.dtOffset = 0 + self.fFlag = 0 + self.fUnitSize= 0 + self.gapSize = 0 + self.volSeqNr = 0 + self.lenFi = 0 + self.fIdentifier = "" + self.sysUseStar = 0 + self.suspBuf = "" + self.rrip = None + +class PathTabelItem(Structure): + def __init__(self): + self.lenDi = 0 + self.lenEattr = 0 + self.locExtenti = 0 + self.pdirNr = 0 + self.fIdentifier = "" + +class ISO9660: + """ + This class can dump iso9660 file system meta data and extract files. + Support: + RRIP extension. + """ + + def __init__(self, isofile): + try: + f = open(isofile, 'rb') + except(IOError): + sys.stderr.write("can't open {0}".format(isofile)) + sys.exit(-1) + + self.isoFile = f + self.priVol = None + self.rootDir = None + self.rripOffset = -1 + + desc_nr = 0 + while True: + desc_nr = desc_nr + 1 + try: + self.isoFile.seek(BLOCK_SIZE*(15+desc_nr)) + volume_dsc = self.isoFile.read(BLOCK_SIZE) + flag = struct.unpack('B',volume_dsc[0:1])[0] + if flag == 1: + self.__readPrimaryVolume__(volume_dsc) + continue + if flag == 255: + break + except Exception as e: + gen.log("Got exception when init iso file:", sys.exc_info()[0]) + self.priVol = None + self.rootDir = None + break + + def __del__(self): + self.isoFile.close() + + def __readPrimaryVolume__(self, volume_dsc): + """ Dump primary volume descriptor """ + global BLOCK_SIZE + priVol = PrimaryVolume() + priVol.sysIdentifier = volume_dsc[8:40] + priVol.volIdentifier = volume_dsc[40:72] + priVol.volSize = struct.unpack(' 0: + entry_buf = desc_buf[self.rripOffset:] + gen.log ("__rripLoop__ offset:%d"%(self.rripOffset)) + else: + entry_buf = desc_buf + + rr = Rrip() + while True: + ce_blk = 0 + ce_len = 0 + ce_off = 0 + head = 0 + len_entry = 0 + + while True: + #gen.log (("\n%d, %d\n")%(len_buf, head)) + head += len_entry + if len_buf - head < 4: # less than one entry + break + entry_buf = entry_buf[len_entry:] + + sig1 = struct.unpack("B", entry_buf[0:1])[0] + sig2 = struct.unpack("B", entry_buf[1:2])[0] + len_entry = struct.unpack("B", entry_buf[2:3])[0] + ver = struct.unpack("B", entry_buf[3:4])[0] + #if len_entry == 0: + # gen.log "Got a entry in __rripLoop__ (%c,%c) of SUSP with length:(%d),version:(%d)-->"%(sig1,sig2,len_entry, ver), + if len_entry == 0: + break; + + if sig1 == ord('S') and sig2 == ord('P'): + ck1 = struct.unpack("B", entry_buf[4:5])[0] + ck2 = struct.unpack("B", entry_buf[5:6])[0] + skip = struct.unpack("B", entry_buf[6:7])[0] + #gen.log "-->(0x%x==0xBE,0x%x==EF,%d)" %(ck1, ck2, skip) + if ck1 == 0xBE and ck2 == 0xEF: + rr.offset = skip + continue + + if sig1 == ord('C') and sig2 == ord('E'): + ce_blk = struct.unpack("(%d,%d,%d)" %(ce_blk, ce_off, ce_len) + continue + + if sig1 == ord('N') and sig2 == ord('M'): + flag = struct.unpack("B", entry_buf[4:5])[0] + #gen.log "-->(flag:(0x%x), name:(%s))" %(flag, entry_buf[5:len_entry]) + if flag == 0x02: # FLAG_CURRENT + rr.altname += "." + elif flag == 0x04: # FLAG_PARENT + rr.altname += ".." + elif flag == 0x01 or flag ==0: # 1:FLAG_CONTINUE + rr.altname += entry_buf[5:len_entry].decode() + continue + + if sig1 == ord('P') and sig2 == ord('N'): + rr.devH = struct.unpack(" 0: + #gen.log " Read CE block, (%d, %d, %d)"%(ce_blk, ce_len, ce_off) + self.isoFile.seek(ce_blk*BLOCK_SIZE + ce_off) + entry_buf = self.isoFile.read(ce_len) + len_buf = ce_len + else: + break + # while (True) end # + return rr + + def checkISOBootable(self): + """ Struct of a classical generic MBR. + + 0x0000 Bootstrap Code area + ----------------------------------------- + 0x01BE + .. Partition table + 0x01EE + ------------------------------------------ + 0x01FE 55h + Boot signature + 0x01FF AAh + + """ + self.isoFile.seek(0x01FE) + h = self.isoFile.read(2) + s1 = struct.unpack('B', h[0:1])[0] + s2 = struct.unpack('B', h[1:2])[0] + + #gen.log "-->(0x%x,0x%x)" %(s1, s2) + + if (s1 == 0x55) and (s2 == 0xAA): + result = True # "Bootable" + else: + result = False # "Not bootable" + + return result + + def searchDir(self, path): + # /root/abc/ - ['', 'root', 'abc', ''] + # /root/abc - ['', 'root', 'abc'] + # / - ['', ''] + dircomps = path.split('/') + if dircomps[-1] == '': + dircomps.pop() + if dircomps == []: + return + + if self.priVol == None: + return + + if len(dircomps) == 1: + return self.rootDir + + pdir_loc = self.priVol.rootLoc + pdir_len = self.priVol.rootTotal + i_dircomp = 1 + + while True: + found = False + dirs = self.readDirItems(pdir_loc, pdir_len) + for item in dirs: + if item.fIdentifier == dircomps[i_dircomp]: + pdir_loc = item.locExtent + pdir_len = item.lenData + found = True + #gen.log "found (%s)"%(dircomps[i_dircomp]) + break + if found: # advacne + if i_dircomp < len(dircomps)-1: + i_dircomp = i_dircomp + 1 + else: + return item + else: + gen.log ("can't find " + dircomps[i_dircomp]) + return None + + def readDirrecord(self, desc_buf): + """ Dump file dirctory record + Return a directory record reading from a Directory Descriptors. + """ + dirRec = DirRecord() + try: + dirRec.lenDr = struct.unpack("B", desc_buf[0:1])[0] + if dirRec.lenDr == 0: + return None + except: + return None + + dirRec.lenEattr = struct.unpack("B", desc_buf[1:2])[0] + dirRec.locExtent = struct.unpack(" dirRec.sysUseStar+4: + if dirRec.locExtent == self.priVol.rootLoc: + dirRec.suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + if self.rripOffset != -1: + rripNode = self.__rripLoop__(suspBuf, dirRec.lenDr-dirRec.sysUseStar) + dirRec.rrip = rripNode + if rripNode != None: + if rripNode.altname != "": + dirRec.fIdentifier = rripNode.altname + dirRec.lenFi = len(rripNode.altname) + #gen.log "rrip_altname: %s"%(dirRec.fIdentifier) + # if rripNode end # + # if self.rripOffset != -1 end # + # if dirRec.lenDr > .. end # + return dirRec + + def readDirItems(self, block_nr=None, total=None): + """ Read file dirctory records + Read dirctory records from 'block_nr' with a length of 'total'. + Return a list containing directory records(DirRecord). + """ + dirs = [] + total_blk = (total+BLOCK_SIZE-1)//BLOCK_SIZE + i_blk = 0 + while i_blk < total_blk: + self.isoFile.seek((block_nr+i_blk)*BLOCK_SIZE) + desc_buf = self.isoFile.read(BLOCK_SIZE) + i_blk = i_blk + 1 + while True: + dirItem = self.readDirrecord(desc_buf) + if dirItem == None: + break + + dirs.append(dirItem) + if desc_buf.__len__() > dirItem.lenDr: + desc_buf = desc_buf[dirItem.lenDr:] + else: + break + return dirs + + def readPathtableL(self): + """ Read path table of L typde """ + if self.priVol == None: + return + block_nr = self.priVol.ptLRd + total = self.priVol.ptSize + + path_table = [] + self.isoFile.seek(block_nr*BLOCK_SIZE) + ptbuf = self.isoFile.read((BLOCK_SIZE * ((total+BLOCK_SIZE-1)//BLOCK_SIZE))) + i = 0 + r_size = 0 + while True : + i = i+1 + t = PathTabelItem() + + t.lenDi = struct.unpack('B', ptbuf[0:1])[0] + t.lenEattr = struct.unpack('B', ptbuf[1:2])[0] + t.locExtent = struct.unpack('= total: + break + ptbuf = ptbuf[9+t.lenDi-1+len_pd:] + # while True + return path_table + + # @path -- path within iso file system. + # @output -- what local path you want write to. + # @pattern -- regular expression. + # @r -- recursion flag, write the whole sub-directories or not. + # @all_type -- which file type should be writed. + # False: Write regular type files only. + # True: Wirte all types files (regular, device file, link, socket, etc) + def writeDir(self, path, output, pattern="", r=True, all_type=False): + """ Extract a directory + Return 0 means success otherwise failure. + """ + d = self.searchDir(path) + if d != None: + if output.endswith("/"): + output = output[0:-1] + # Try to make target directory. + if not os.path.exists(output): + try: + os.makedirs(output) + except(OSError): + sys.stderr.write("can't make dirs({0})\n".format(output)) + return E_FAILURE + pp = None + if pattern != "": + p = r'{0}'.format(pattern) + pp = re.compile(p) + #gen.log "writeDir: flag(%x)"%(d.fFlag) + if d.fFlag & 0x02 == 0x02: + # Check if a clean directory. + #try: + # if len(os.listdir(output)) > 0: + # sys.stderr.write("The target directory is not empty\n") + # return E_FAILURE + #except(OSError): + # sys.stderr.write("can't access dirs({0})\n".format(p)) + # return E_FAILURE + self.writeDir_r(output, d, pp, r, all_type) + return E_SUCCESS + else: + return self.writeFile(d, output+path, all_type) + else: + return E_FAILURE + + def writeDir_r(self, det_dir, dire, pp, r, all_type): + #gen.log "writeDir_r:(%s)"%(det_dir) + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + if (pp != None) and (pp.search(d.fIdentifier) == None): + match = False + else: + match = True + #gen.log "mathing %s, %s, (%x)"%(match, d.fIdentifier, d.fFlag) + p = det_dir + "/" + d.fIdentifier + if d.fFlag & 0x02 == 0x02: + if not os.path.exists(p): + os.makedirs(p, 0o777) + if r: + if match: + self.writeDir_r(p, d, None, r, all_type) # Don't need to match subdirectory. + else: + self.writeDir_r(p, d, pp, r, all_type) + elif match: + self.writeFile(d, p, all_type) + # if not d.fIdentifier end # + # for d in dirs end # + + def writeFile(self, dirRec, detFile, all_type): + """ Write a file to detFile + Return 0 means success otherwise failure. + """ + global file_out + if detFile == "" or dirRec == None: + sys.stderr.write("can't write file\n") + return E_FAILURE + + #gen.log "write file (%s)"%(detFile) + config.status_text = detFile + + dirname = os.path.dirname(detFile) + if not os.path.exists(dirname): + try: + os.makedirs(dirname, 0o777) + except(OSError): + sys.stderr.write("can't makedirs\n") + return E_FAILURE + + if all_type == True: + # device file + if dirRec.rrip != None and (dirRec.rrip.devH != 0 or dirRec.rrip.devL != 0): + #fFlag == 0 + high = dirRec.rrip.devH + low = dirRec.rrip.devL + if high == 0: + device = os.makedev(os.major(low), os.minor(low)) + else: + device = os.makedev(high, os.minor(low)) + try: + mode = dirRec.rrip.fMode & 0o770000 + if mode == S_IFCHR: + os.mknod(detFile, 0o777|stat.S_IFCHR, device) + elif mode == S_IFBLK: + os.mknod(detFile, 0o777|stat.S_IFBLK, device) + except(OSError): + sys.stderr.write("can't mknode, maybe no permission\n") + return E_DEVICEFILE + + return E_SUCCESS + + loc = dirRec.locExtent + length = dirRec.lenData + self.isoFile.seek(BLOCK_SIZE * loc) + #gen.log "file length(%d)"%(length) + r_size = BLOCK_SIZE*1024*50 #100M cache + + try: + f_output = open(detFile, 'wb', r_size) + except(IOError): + sys.stderr.write("can't open{0} for write\n".format(detFile)) + return E_FAILURE + + while True: + if length == 0: + break + elif length <= r_size: + r_size = length + length = 0 + else: + length = length - r_size + + buf = self.isoFile.read(r_size) + f_output.write(buf) + f_output.flush() + # while True end. + f_output.close() + return E_SUCCESS + + def readDir(self, dir_path, r=True): + file_list = [] + d = self.searchDir(dir_path) + if d != None: + if (d.fFlag & 0x02) == 0x02: + #gen.log "readDir (%x, %x)"%(d.locExtent, d.lenData) + if dir_path.endswith("/"): + dir_path = dir_path[0:-1] + self.readDir_r(file_list, dir_path, d, r) + # if (d.fFlag & 0x02) == 0x02: # + # if d != None: + return file_list + + def readDir_r(self, file_list, dir_path, dire, r): + if (dire.fFlag & 0x02) != 0x02: + return + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + p = dir_path + "/" + d.fIdentifier + file_list.append(p) + if r: + self.readDir_r(file_list, p, d, r) + # if not d.fIdentifier # + # for d in dirs: # + + def checkIntegrity(self): + if self.priVol == None: # no primary volume + return False + + if self.priVol.ptSize == 0: # empty ? + return True + + path_table = self.readPathtableL() + if path_table == []: # pathtable record is broken. + return False + + # find last file item to check + for dr in reversed(path_table): + #gen.log dr.fIdentifier + dirs = self.readDirItems(dr.locExtent, BLOCK_SIZE) + if len(dirs) > 2: + dot = dirs[0] + dirs2 = self.readDirItems(dot.locExtent, dot.lenData) # get the whole items. + for dr2 in reversed(dirs2): # search last file item. + if dr2.fFlag == 0: + #gen.log "get last file(%s)"%(dr2.fIdentifier) + try: + #self.isoFile.seek(BLOCK_SIZE * dr2.locExtent+dr2.lenData) + lastfile_end = BLOCK_SIZE * dr2.locExtent + dr2.lenData + self.isoFile.seek(0, os.SEEK_END) + iso_end = self.isoFile.tell() + #gen.log ("%d-->%d")%(lastfile_end, iso_end) + if iso_end >= lastfile_end: + return True + else: + return False + except(IOError): + #gen.log "exception when seek. iso is broken" + return False + elif len(dirs) < 2: # Dir record is broken. At least, should have two entries. + return False + return True + +########################################################################### +def dump_dir_record(dirs): + """ Dump all the file dirctory records contained in desc_buf """ + + gen.log("Dump file/deirectory record") + gen.log("===========================", end="\n") + if dirs != None: + for f in dirs: + gen.log("length of directory record:(0x%x), length of extend attribute:(%d), \ +location of record:(%d)BLOCK->(0x%x), data length(%d) size of file unit:(%d), \ +interleave gap size:(%d), file flag:(0x%x),name length:(%d) identify:(%s)\n" \ +%(f.lenDr, f.lenEattr, f.locExtent, f.locExtent*BLOCK_SIZE,f.lenData, \ + f.fUnitSize, f.gapSize, f.fFlag, f.lenFi, f.fIdentifier)) + +def dump_pathtable_L(path_table): + """ Dump path table of L typde """ + + gen.log("Dump path table") + gen.log("================", end="\n") + #path_table = readPathtableL() + i = 0 + for t in path_table: + i = i + 1 + if t.lenDi == 1: + if t.fIdentifier in [0, 1]: + gen.log("is a root directory(%d)" %(is_root)) + gen.log("%d->length of identify:(%d), length of extend attribute:(%d), \ +local:(%d)->(0x%x), parent dir number:(%d), identify:(%s)\n" \ +%(i, t.lenDi, t.lenEattr, t.locExtent, t.locExtent*BLOCK_SIZE, t.pdirNr, t.fIdentifier)) + +def dump_primary_volume(privol=None): + """ Dump primary volume descriptor """ + + if privol == None: + gen.log("Can't dump, maybe iso is broken") + return + gen.log("===== Dump primary volume descriptor ==") + + gen.log("System Identifier:(%s)" %(privol.sysIdentifier.decode())) + gen.log("Volume Identifier:(%s)" %privol.volIdentifier.decode()) + gen.log("Volume Space size:(0x%x)BLOCKS(2kB)" %privol.volSize) + gen.log("Volume sequence number:(%d)" %(privol.volSeq)) + gen.log("logic block size:(0x%x)" %(privol.blockSize)) + gen.log("Volume path talbe L's BLOCK number is :(0x%x-->0x%x), size(%d)" %(privol.ptLRd, privol.ptLRd*BLOCK_SIZE, privol.ptSize)) +# gen.log "Abstract File Identifier: (%s)" %(volume_dsc[739:776]) +# gen.log "Bibliographic File Identifier: (%s)" %(volume_dsc[776:813]) + gen.log("pathtable locate (%d)" %(privol.ptLRd)) + gen.log("File Structure Version:(%d)" %(privol.fsVer)) + gen.log("Root directory is at (%d)block, have(0x%x)bytes" %(privol.rootLoc, privol.rootTotal)) +# dump_dir_record(None, 23, 1) + +def dump_boot_record(volume_dsc): + """ Dump boot record """ + + gen.log("===== Dump boot record ==") + std_identifier = volume_dsc[1:6] + gen.log("Standard Identifier:(%s)" %std_identifier) + + vol_ver = struct.unpack('B', volume_dsc[6]) + gen.log("Volume descriptor version:(%d)" %vol_ver) + + bootsys_identifier = volume_dsc[7:39] + gen.log("boot system identifier(%s)" %bootsys_identifier) + + boot_identifier = volume_dsc[39:71] + gen.log("boot identifier(%s)" %boot_identifier) + +def usage(): + """ Prompt user how to use """ + gen.log(""" +Usage: isodump dump-what [options] iso-file + [dump-what] + ----------- + boot - Dump boot record. + primary-volume - Dump primary volume. + pathtable - Dump path table. + dir-record [block number] [length] - Dump a raw data of a Directory Record + + iso:/dir [-r] [-o output] [-p pattern] - Dump a dirctory or file to [output] + -r recursively visit directory. + -p spcify a Regular expression pattern for re.search(pattern,). + +isodump xx.iso - Dump the root directory +isodump pathtable xx.iso - Dump the path table record. + +isodump iso:/ -r xx.iso + -- Dump the root directory of xx.iso recursively. + +isodump iso:/ -r -o /tmp/iso xx.iso + -- Extract the iso to /tmp/iso/. + +isodump iso:/boot -o /tmp/iso/boot xx.iso + -- Extract the /boot directory of xx.iso to /tmp/iso/boot. + +isodump iso:/boot/grup.cfg -o /tmp/grub.cfg xx.iso + -- Extract the file "grup.cfg" to "/tmp/grub.cfg" + +isodump iso:/boot -r -o /tmp/iso -p "*.cfg" xx.iso + -- Extract any files or directories under /boot maching "*.cfg" to /tmp/iso/. +""") + sys.exit(-1) + +if __name__ == '__main__': + argv = sys.argv + if len(argv) < 3: + usage() + + iso9660fs = ISO9660(argv[-1]) + integrity = iso9660fs.checkIntegrity() + if integrity == False: + gen.log("iso file is broken") + sys.exit(-1) + + dump_what = argv[1] + + if dump_what == "primary-volume": + dump_primary_volume(iso9660fs.priVol) + elif dump_what == "pathtable": + path_table = iso9660fs.readPathtableL() + dump_pathtable_L(path_table) + if dump_what == "dir-record": + if len(argv) == 5: + gen.log("dump dir-record (%s, %s)"%(argv[2], argv[3])) + dirs = iso9660fs.readDirItems(int(argv[2]), int(argv[3])) + dump_dir_record(dirs) + else: + usage() + elif dump_what.startswith("iso:"): + o_path = "" + r = False + o = False + p = False + pattern = "" + for arg in argv[2:-1]: + if arg == "-r": + r = True + o = False + p = False + elif arg == "-o": + o = True + p = False + elif arg == "-p": + o = False + p = True + elif o == True: + o_path = arg + o = False + elif p == True: + pattern = arg + p = False + + isodir = dump_what[4:] + if o_path == "": + gen.log("dump_dir(%s)"%(isodir)) + filelist = iso9660fs.readDir(isodir, r) + if filelist == []: + gen.log("can't read any file from (%s)"%(isodir)) + else: + for f in filelist: + gen.log(f) + else: + gen.log("writeDir(%s)->(%s) with pattern(%s)"%(isodir, o_path, pattern)) + sys.exit(iso9660fs.writeDir(isodir, o_path, pattern, r, True)) + diff --git a/deb_dist/multibootusb-8.5.0/scripts/mbusb_cli.py b/deb_dist/multibootusb-8.5.0/scripts/mbusb_cli.py new file mode 100644 index 0000000..c8ef675 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/mbusb_cli.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_cli.py +# Purpose: Module to handle command line options of multibootusb +# 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 re +import shutil +from . import usb +from . import gen +from .iso import * +from .uninstall_distro import * +from .distro import * +from .syslinux import * +from .install import * + + +def read_input_uninstall(): + response = False + try: + response = int(input("Please enter the number against the distro you need to uninstall: ")) + except ValueError: + log('\nPlease provide valid integer from the above list.\n') + + return response + + +def cli_install_distro(): + ''' + if platform.system() == 'Linux': + if os.getuid() != 0: + exit("You need to have root privileges to run this script.\nPlease try again using 'sudo'. Exiting.") + elif platform.system() == 'Windows': + + if admin.isUserAdmin(): + admin.elevate() + ''' + + log('Starting multibootusb from Command line...') + if usb.is_block(config.usb_disk) is False: + log(config.usb_disk, 'is not a valid device partition...') + exit(1) + elif integrity(config.iso_link) is not True: + log(config.iso_link, ' failed to pass integrity check...') + exit(1) + elif size_not_enough(config.iso_link, config.usb_disk) is True: + log(config.usb_disk, 'does not have enough space...') + else: + prepare_mbusb_host_dir() + extract_cfg_file(config.iso_link) + _distro = distro(iso_cfg_ext_dir(), config.iso_link) + log('Detected distro type is', _distro) + if _distro is not None: + log('\nSelected ISO is :', quote(iso_name(config.iso_link))) + log('Selected target device is:', quote(config.usb_disk), '\n') + log('Please confirm the option.') + log('Y/y/Yes/yes/YES or N/n/No/no/NO') + if read_input_yes() is True: + config.distro = _distro + copy_mbusb_dir_usb(config.usb_disk) + install_progress() + syslinux_distro_dir(config.usb_disk, config.iso_link, _distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, _distro) + else: + log('Sorry', iso_name(config.iso_link), 'is not supported at the moment\n' + 'Please report tissue at https://github.com/mbusb/multibootusb/issues') + + +def cli_uninstall_distro(): + distro_list = install_distro_list() + if distro_list is not None: + for index, _distro_dir in enumerate(distro_list): + log(index, '--->>', _distro_dir) + user_input = read_input_uninstall() + if user_input is not False: + for index, _distro_dir in enumerate(distro_list): + if index == user_input: + config.uninstall_distro_dir_name = _distro_dir + unin_distro() + else: + log('No distro installed on', config.usb_disk) diff --git a/deb_dist/multibootusb-8.5.0/scripts/mbusb_gui.py b/deb_dist/multibootusb-8.5.0/scripts/mbusb_gui.py new file mode 100644 index 0000000..ed0e5ba --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/mbusb_gui.py @@ -0,0 +1,640 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_gui.py +# Purpose: Module to handle multibootusb through gui +# 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 platform +import sys +import signal +from PyQt5 import QtCore, QtGui, QtWidgets +import subprocess +import time +from scripts.gui.ui_multibootusb import Ui_Dialog +from . import usb +from .gen import * +from .install import * +from .uninstall_distro import * +from .syslinux import * +from .distro import * +from .iso import * +from .imager import Imager, dd_linux, dd_win +from . import persistence +from . import config +from . import admin +from . import qemu +from .update_cfg_file import update_distro_cfg_files + + +class AppGui(qemu.Qemu, Imager, QtWidgets.QDialog, Ui_Dialog): + """ + Main multibootusb GUI manipulation class. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + # Main Tab + self.ui.checkBox_all_drives.clicked.connect(self.add_device) + self.ui.detect_usb.clicked.connect(self.onRefereshClick) + self.ui.close.clicked.connect(self.on_close_Click) + self.ui.browse_iso.clicked.connect(self.browse_iso) + self.ui.comboBox.activated[str].connect(self.onComboChange) + # self.ui.create.clicked.connect(self.update_progress) + self.ui.create.clicked.connect(self.onCreateClick) + self.ui.slider_persistence.valueChanged.connect(self.update_slider_text) + self.ui.uninstall.clicked.connect(self.OnUninstallClick) + + # ISO Imager Tab + self.ui.pushButton.clicked.connect(self.on_Imager_Browse_iso_Click) + self.ui.comboBox_2.activated[str].connect(self.onImagerComboChange) + self.ui.pushbtn_imager_refreshusb.clicked.connect(self.onRefereshClick) + self.ui.imager_close.clicked.connect(self.on_close_Click) + self.ui.imager_write.clicked.connect(self.dd_write) + + # Syslinux Tab + self.ui.install_syslinux.clicked.connect(self.onInstall_syslinuxClick) + self.ui.edit_syslinux.clicked.connect(self.onedit_syslinux) + + # QEMU Tab + self.ui.browse_iso_qemu.clicked.connect(self.on_Qemu_Browse_iso_Click) + self.ui.boot_iso_qemu.clicked.connect(self.on_Qemu_Boot_iso_Click) + self.ui.boot_usb_qemu.clicked.connect(lambda: self.on_Qemu_Boot_usb_Click(str(self.ui.comboBox.currentText()))) + # self.ui.tabWidget.removeTab(3) + + # Update progressbar and status (Main ISO install) + self.progress_thread_install = GuiInstallProgress() + self.progress_thread_install.finished.connect(self.install_syslinux) + self.progress_thread_install.update.connect(self.ui.progressBar.setValue) + self.progress_thread_install.status.connect(self.ui.status.setText) + + # Update progressbar and status (Uninstall from previous install) + self.progress_thread_uninstall = GuiUninstallProgress() + self.progress_thread_uninstall.finished.connect(self.uninstall_sys_file_update) + self.progress_thread_uninstall.update.connect(self.ui.progressBar.setValue) + self.progress_thread_uninstall.status.connect(self.ui.status.setText) + + # Update progressbar and status (dd ISO) + self.progress_thread_dd = DD_Progress() + self.progress_thread_dd.update.connect(self.ui.imager_progressbar.setValue) + self.progress_thread_dd.finished.connect(self.dd_finished) + self.progress_thread_dd.status.connect(self.ui.imager_label_status.setText) + + self.add_device() + prepare_mbusb_host_dir() + + def add_device(self): + """ + Adds list of available USB devices to GUI combobox. + :return: + """ + self.ui.comboBox.clear() + self.ui.comboBox_2.clear() + if self.ui.checkBox_all_drives.isChecked(): + detected_device = usb.list(partition=1, fixed=True) + else: + detected_device = usb.list() + if bool(detected_device): + for device in detected_device: + self.ui.comboBox.addItem(str(device)) + if self.ui.comboBox.currentText(): + self.onComboChange() + + imager_detected_device = self.imager_list_usb(partition=0) + if bool(imager_detected_device): + for disk in imager_detected_device: + self.ui.comboBox_2.addItem(str(disk)) + self.onImagerComboChange() + + def onComboChange(self): + """ + Detects and updates GUI with populated USB device details. + :return: + """ + self.ui.listWidget.clear() + config.usb_disk = str(self.ui.comboBox.currentText()) + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.usb_disk): + self.update_gui_oncombobox(config.usb_disk) + else: + log("No USB disk found...") + + def onRefereshClick(self): + """ + Calls function to detect USB devices. + :return: + """ + self.ui.comboBox.clear() + self.ui.comboBox_2.clear() + self.add_device() + + def update_gui_oncombobox(self, usb_disk): + self.usb_details = usb.details(usb_disk) + config.usb_mount = self.usb_details['mount_point'] + self.ui.usb_dev.setText("Drive :: " + usb_disk) + # self.label.setFont(QtGui.QFont("Times",weight=QtGui.QFont.Bold)) + if platform.system() == 'Windows': + self.ui.usb_vendor.setText("FileSystem :: " + self.usb_details['file_system']) + self.ui.usb_model.setText("Label :: " + self.usb_details['label']) + else: + self.ui.usb_vendor.setText("Vendor :: " + self.usb_details['vendor']) + self.ui.usb_model.setText("Model :: " + self.usb_details['model']) + self.ui.usb_size.setText("Total Size :: " + str(usb.bytes2human(self.usb_details['size_total']))) + self.ui.usb_mount.setText("Mount :: " + self.usb_details['mount_point']) + self.update_list_box(usb_disk) + + + def update_list_box(self, usb_disk): + """ + Updates listbox with installed distros on selected USB disk. + :param usb_mount: Selected USB disk from combobox. + :return: + """ + distro_list = install_distro_list() + #sys_cfg_file = os.path.join(str(usb_mount), "multibootusb", "syslinux.cfg") + if distro_list is not None: + self.ui.listWidget.clear() + for name in distro_list: + self.ui.listWidget.addItem(name) + else: + if config.usb_mount == 'No_Mount': + log("UBS disk is not mounted and can't update list widget...") + #QtWidgets.QMessageBox.information(self, 'No Install...', + # 'syslinux.cfg does not exist for updating list widget.') + + def browse_iso(self): + if str(self.ui.lineEdit.text()): + self.ui.lineEdit.clear() + config.iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', '', 'ISO Files (*.iso)')[0] + if config.iso_link: + if platform.system() == "Windows": + if "/" in config.iso_link: + config.iso_link = config.iso_link.strip().replace("/", "\\") + self.ui.lineEdit.insert(str(config.iso_link)) + if os.path.exists(config.iso_link): + clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned. + extract_cfg_file(config.iso_link) + config.distro = distro(iso_cfg_ext_dir(), config.iso_link) # Detect supported distro + if config.distro: + per_availability = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[0] + per_max_size = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[1] + if per_availability is not None: + self.ui.slider_persistence.setEnabled(True) + self.ui.slider_persistence.setTickInterval(10) + self.ui.slider_persistence.setSingleStep(10) + ui_per_max_size = per_max_size / 1024 / 1024 + # config.persistence = per_max_size + self.ui.slider_persistence.setMaximum(ui_per_max_size) + log('Persistence Max Size: ' + str(bytes2human(per_max_size))) + else: + log('Persistence is not available for ' + iso_name(config.iso_link)) + else: + log("File not selected...") + + def update_slider_text(self): + slide_value = self.ui.slider_persistence.value() * 1024 * 1024 + self.ui.label_persistence_value.setText(bytes2human(slide_value)) + config.persistence = slide_value + + def install_syslinux(self): + """ + Function to install syslinux on distro directory and on selected USB disks. + :return: + """ + self.ui.status.setText(str("Installing Syslinux...")) + syslinux_distro_dir(config.usb_disk, config.iso_link, config.distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, config.distro, config.persistence) + self.update_list_box(config.usb_disk) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Finished...', iso_name(config.iso_link) + ' has been successfully installed.') + config.process_exist = None + + def onInstall_syslinuxClick(self): + """ + Function to install syslinux/extlinux on selected USB disk. + :return: + """ + if platform.system() == "Linux" or platform.system() == "Windows": + + if self.ui.install_sys_all.isChecked() or self.ui.install_sys_only.isChecked(): + log("Installing default syslinux on ", config.usb_disk) + ret = syslinux_default(config.usb_disk) + if ret is True: + if self.ui.install_sys_all.isChecked(): + log("Copying multibootusb directory to " + config.usb_mount) + for dirpath, dirnames, filenames in os.walk(resource_path(os.path.join("tools", "multibootusb"))): + for f in filenames: + log("Copying " + f) + shutil.copy(resource_path(os.path.join(dirpath, f)), os.path.join(self.usb.get_usb(config.usb_disk).mount, "multibootusb")) + QtWidgets.QMessageBox.information(self, 'Install Success...', + 'Syslinux installed successfully on ' + config.usb_disk) + elif ret is False: + QtWidgets.QMessageBox.information(self, 'Install error...', + 'Sorry. Syslinux failed to install on ' + config.usb_disk) + else: + QtWidgets.QMessageBox.information(self, 'No selection...', + 'Please select one of the option from above.') + + def onedit_syslinux(self): + """ + Function to edit main syslinux.cfg file. + :return: + """ + # Function to edit syslinux.cfg file on editors like gedit, notepad etc. + # Suggest me more editor which can be included in to this function. + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + log("Locating " + sys_cfg_file) + editor = '' + if not os.path.exists(sys_cfg_file): + log("syslinux.cfg file not found...") + QtWidgets.QMessageBox.information(self, 'File not found...', 'Sorry. Unable to locate syslinux.cfg file.\n' + 'You can only edit syslinux.cfg file generated by multibootusb.') + else: + if platform.system() == "Linux": + for e in config.editors_linux: + if subprocess.call('which ' + e, shell=True) == 0: + log("Editor found is " + e) + editor = e + break + elif platform.system() == "Windows": + for e in config.editors_win: + if not shutil.which(e) is None: + log("Editor found is " + e) + editor = e + break + if not editor: + QtWidgets.QMessageBox.information(self, 'Editor not found...', + 'Sorry. Installed editor is not supported by multibootusb\n' + 'Edit ' + sys_cfg_file + ' manually.\n') + else: + try: + subprocess.Popen(editor + " '" + sys_cfg_file + "'", shell=True).pid + except OSError: + QtWidgets.QMessageBox.warning(self, 'Error...', + 'Failed to open syslinux.cfg file.\n' + 'Edit syslinux.cfg file manually.\n') + + def OnUninstallClick(self): + """ + Triggers a function to uninstall a selected distro. + :return: + """ + if self.ui.listWidget.currentItem() is None: + log("Please select a distro from the list.") + QtWidgets.QMessageBox.information(self, 'No selection.', 'Please select a distro from the list.') + else: + config.uninstall_distro_dir_name = str(self.ui.listWidget.currentItem().text()).strip() + reply = QtWidgets.QMessageBox.question(self, "Review selection...", + "Are you sure to uninstall " + config.uninstall_distro_dir_name, + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + + if not os.path.exists(os.path.join(config.usb_mount, 'multibootusb', config.uninstall_distro_dir_name)): + log("Distro install directory not found. Just updating syslinux.cfg file.") + update_sys_cfg_file() + #self.uninstall.update_sys_cfg_file() + else: + self.progress_thread_uninstall.start() + + def uninstall_sys_file_update(self): + """ + Function to remove and update uninstall distro text. + :return: + """ + update_sys_cfg_file() + self.update_list_box(config.usb_mount) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Uninstall Complete...', + config.uninstall_distro_dir_name + ' has been successfully removed.') + + def onCreateClick(self): + """ + Main function to create bootable USB disk. + :param usb_disk: ComboBox text as detected USB disk. + :param iso_link: LineEdit text as selected ISO link. + :return: + """ + if not config.usb_disk: + log("No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.") + QtWidgets.QMessageBox.information(self, "No Device...", + "No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.") + elif not config.iso_link: + log("No ISO found.\n\nPlease use step 2 to choose an ISO.") + QtWidgets.QMessageBox.information(self, "No ISO...", "No ISO found.\n\nPlease use step 2 to choose an ISO.") + elif usb.details(config.usb_disk)['mount_point'] == 'No_Mount': + log("USB disk is not mounted.\nPlease mount USB disk and press refresh USB button.") + QtWidgets.QMessageBox.information(self, "No Mount...", "USB disk is not mounted.\n" + "Please mount USB disk and press refresh USB button.") + else: + # clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned. + # extract_cfg_file(config.iso_link) # Extract files from ISO + # config.distro = distro(iso_cfg_ext_dir(), config.iso_link) # Detect supported distro + usb_details = usb.details(config.usb_disk) + log("USB Disk is " + config.usb_disk) + log("USB Label is " + config.usb_label) + log("USB UUID is " + config.usb_uuid) + log("USB Mount path is " + config.usb_mount) + log("Total size of the disk is " + str(usb.bytes2human(usb_details['size_total']))) + log("Total used size is " + str(usb.bytes2human(usb_details['size_used']))) + log("Total size left on the disk is " + str(usb.bytes2human(usb_details['size_free']))) + log("FileSystem is " + usb_details['file_system']) + log("Vendor is " + usb_details['vendor']) + log("Model is " + usb_details['model']) + log("Name of the ISO file is " + iso_name(config.iso_link)) + + if os.path.exists(config.iso_link): + self.ui.lineEdit.clear() + if config.distro: + log("Distro type detected is " + config.distro) + copy_mbusb_dir_usb(config.usb_disk) + if not os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link))): + config.persistence = self.ui.slider_persistence.value() * 1024 * 1024 + install_size = iso_size(config.iso_link) + config.persistence + log("Persistence choosen is " + str(config.persistence) + " MB") + if install_size >= disk_usage(config.usb_mount).free: + QtWidgets.QMessageBox.information(self, "No Space.", "No space available on " + + config.usb_disk) + else: + reply = QtWidgets.QMessageBox.question(self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.usb_disk + + 'USB mount point:: %s\n' % config.usb_mount + + 'Selected distro:: %s\n\n' % iso_name(config.iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.ui.slider_persistence.setEnabled(False) + config.process_exist = True + self.progress_thread_install.start() + + else: + QtWidgets.QMessageBox.information(self, 'Already Exist...', + os.path.basename(config.iso_link) + ' is already installed.') + else: + QtWidgets.QMessageBox.information(self, 'No support...', + 'Sorry.\n' + os.path.basename(config.iso_link) + + ' is not supported at the moment.\n' + 'Please email this issue to feedback.multibootusb@gmail.com') + + # Added to refresh usb disk remaining size after distro installation + # self.update_gui_usb_info() + + def dd_finished(self): + """ + Re-enable the blocked widgets for newer use. + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + self.ui.imager_bootable.setText("Bootable ISO :: ") + self.ui.imager_iso_size.setText("ISO Size :: ") + config.process_exist = None + QtWidgets.QMessageBox.information(self, 'Finished...', 'ISO has been written to USB disk.\nPlease reboot your ' + 'system to boot from USB.') + + def dd_start(self): + """ + Function to block the widgets under ISO Imager tab... + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.lineEdit_3.clear() + self.ui.pushButton.setEnabled(False) + self.ui.comboBox_2.setEnabled(False) + self.ui.pushbtn_imager_refreshusb.setEnabled(False) + status_text = ("Writing " + os.path.basename(config.imager_iso_link) + "" + " to " + "" + + config.imager_usb_disk_selected + "") + self.ui.imager_label_status.setText(status_text) + + def dd_quit(self): + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + QtWidgets.QMessageBox.information(self, 'Failed!', 'Writing ISO failed.') + + def dd_write(self): + if not config.imager_usb_disk: + QtWidgets.QMessageBox.information(self, 'No USB...', 'Please Insert USB disk and rerun multibootusb.') + elif not config.imager_iso_link: + QtWidgets.QMessageBox.information(self, 'No ISO...', 'Please select an ISO.') + else: + usb_disk_size = int(self.imager_usb_detail(config.imager_usb_disk, partition=0).total_size) + self.iso_size = os.path.getsize(config.imager_iso_link) + if self.iso_size >= usb_disk_size: + QtWidgets.QMessageBox.information(self, "No Space.", os.path.basename(config.imager_iso_link) + + " size is larger than the size of " + config.imager_usb_disk) + else: + reply = QtWidgets.QMessageBox.question \ + (self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.imager_usb_disk + + 'Selected distro:: %s\n\n' % os.path.basename(config.imager_iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.dd_start() + config.process_exist = True + self.progress_thread_dd.start() + + def on_close_Click(self): + """ + Closes main GUI. + :return: + """ + self.close() + + def closeEvent(self, event): + """ + To capture the main close event. + :param event: Close event. + :return: + """ + if config.process_exist == None: + event.accept() + else: + reply = QtWidgets.QMessageBox.question(self, 'Exit MultiBootUSB...', + "A process is still running.\n" + "Do you really want to quit multibootusb?", QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + log("Closing multibootusb...") + event.accept() + sys.exit(0) + else: + log("Close event cancelled.") + event.ignore() + + +class GuiInstallProgress(QtCore.QThread): + """ + Update GUI thread during install. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + def __del__(self): + self.wait() + + def run(self): + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + self.thread = GenericThread(install_progress) + status_text = "" + self.thread.start() + while self.thread.isRunning(): + if config.status_text.strip(): + config.status_text = config.status_text.replace(install_dir + "/", "Extracting ") + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + self.status.emit("Installing boot loader...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + log("Distro extraction completed...") + + return + + +class GuiUninstallProgress(QtCore.QThread): + """ + Update GUI thread during uninstall. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + self.thread = GenericThread(uninstall_progress) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "Please wait..." + self.update.emit(100) + self.update.emit(0) + config.percentage = 0 + self.status.emit("Updating syslinux.cfg file...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + log("Distro uninstall is complete...") + + return + + +class DD_Progress(QtCore.QThread): + """ + Update GUI progress bar without blocking rest of GUI element when dd process is in progress. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + if platform.system() == 'Linux': + self.thread = GenericThread(dd_linux) + elif platform.system() == 'Windows': + self.thread = GenericThread(dd_win) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + if config.imager_percentage: + self.update.emit(config.imager_percentage) + if not self.thread.isFinished() and config.percentage == 100: + config.imager_status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + return + + +class GenericThread(QtCore.QThread): + + def __init__(self, function, *args, **kwargs): + QtCore.QThread.__init__(self) + self.function = function + self.args = args + self.kwargs = kwargs + + def __del__(self): + self.wait() + + def run(self): + self.function(*self.args, **self.kwargs) + return + + +def show_admin_info(): + """ + Show simple information box reminding user to run the software with admin/root privilege. + Only required under Linux as the windows executable always will start with admin privilege. + :return: + """ + msg = QtWidgets.QMessageBox() + msg.setIcon(QtWidgets.QMessageBox.Information) + msg.setText('Admin privilege is required to run multibootusb.\n If you are running from source try ' + '\'sudo python3 ./multibootusb\'\n or you can try \'multibootusb-pkexec\' (post install)') + msg.exec_() + + +def main_gui(): + app = QtWidgets.QApplication(sys.argv) + window = AppGui() + ui = Ui_Dialog() + window.show() + window.setWindowTitle("MultiBootUSB - " + mbusb_version()) + window.setWindowIcon(QtGui.QIcon(resource_path(os.path.join("data", "tools", "multibootusb.png")))) + if platform.system() == 'Linux': + if os.getuid() != 0: + show_admin_info() + sys.exit(app.exec_()) diff --git a/deb_dist/multibootusb-8.5.0/scripts/persistence.py b/deb_dist/multibootusb-8.5.0/scripts/persistence.py new file mode 100644 index 0000000..66925e8 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/persistence.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: persistence.py +# Purpose: Module to deal with persistence of a selected distro. +# 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 sys +import os +import platform +import tarfile +import subprocess +from . import usb +from . import iso +from . import gen +from . import config + + +def persistence_distro(distro, usb_disk, iso_link): + """ + Function to detect if distro can have persistence option. + :param distro: Detected distro name. + :return: Distro name as string or None otherwise. + """ + iso_size = iso.iso_size(iso_link) + fat_max_size = (4096 * 1024 * 1024) + usb_details = usb.details(usb_disk) + usb_sf = usb_details['file_system'] + usb_free_size = usb_details['size_free'] + config.usb_uuid = usb_details['uuid'] + config.usb_label = usb_details['label'] + if usb_sf == 'vfat' or 'FAT32': + if usb_free_size > fat_max_size: + _max_size = fat_max_size + else: + _max_size = usb_free_size + else: + _max_size = usb_free_size + if distro == "ubuntu": + gen.log("Persistence option is available.") + return "ubuntu", _max_size + # FIXME to get debian persistence workable... + # Able to add successfully but unable to keep persistence data. + elif distro == "debian" or distro == "debian-install": + gen.log("Persistence option is available.") + return "debian", _max_size + elif distro == "fedora": + gen.log("Persistence option is available.") + return "fedora", _max_size + else: + return None, None + + +def create_persistence(): + if config.distro == "ubuntu": + fs_name = 'casper-rw' + elif config.distro == 'debian' or config.distro == "debian-install": + fs_name = 'live-rw' + elif config.distro == 'fedora': + fs_name = 'overlay-' + config.usb_label + '-' + config.usb_uuid + + persistence = config.persistence / 1024 / 1024 + + if platform.system() == 'Linux': + mkfs = 'mkfs.ext3' + dd = 'dd' + persistence_mkfs_cmd = mkfs + ' -F ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), + fs_name) + elif platform.system() == 'Windows': + mkfs = gen.quote(gen.resource_path(os.path.join("data", "tools", "mkfs", "mke2fs.exe"))) + dd = gen.quote(gen.resource_path(os.path.join("data", "tools", "dd", "dd.exe"))) + persistence_mkfs_cmd = 'echo y|' + mkfs + ' -b 1024 -L ' + fs_name + ' ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) + + if config.distro == 'fedora': + persistence_dd_cmd = dd + ' if=/dev/zero ' \ + 'of=' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), 'LiveOS', fs_name) + \ + ' bs=1M count=' + str(int(persistence)) + else: + persistence_dd_cmd = dd + ' if=/dev/zero of=' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) +\ + ' bs=1M count=' + str(int(persistence)) + + gen.log('Executing ==>' + persistence_dd_cmd) + config.status_text = 'Creating persistence file...' + + if subprocess.call(persistence_dd_cmd, shell=True) == 0: + gen.log("\nSuccessfully created persistence file...\n") + + if not config.distro == 'fedora': + gen.log('Applying filesystem to persistence file...') + gen.log('Executing ==> ' + persistence_mkfs_cmd) + config.status_text = 'Applying filesystem to persistence file...' + if subprocess.call(persistence_mkfs_cmd, shell=True) == 0: + gen.log("\nSuccessfully applied filesystem...\n") + + +def extract_file(file_path, install_dir): + """ + Function to extract persistence files to distro install directory. + :param file_path: Path to persistence file. + :param install_dir: Path to distro install directory. + :return: + """ + tar = tarfile.open(file_path, "r:bz2") + tar.extractall(install_dir) + tar.close() + diff --git a/deb_dist/multibootusb-8.5.0/scripts/progressbar/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/progressbar/__init__.py new file mode 100644 index 0000000..fbab744 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/progressbar/__init__.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Text progress bar library for Python. + +A text progress bar is typically used to display the progress of a long +running operation, providing a visual cue that processing is underway. + +The ProgressBar class manages the current progress, and the format of the line +is given by a number of widgets. A widget is an object that may display +differently depending on the state of the progress bar. There are three types +of widgets: + - a string, which always shows itself + + - a ProgressBarWidget, which may return a different value every time its + update method is called + + - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it + expands to fill the remaining width of the line. + +The progressbar module is very easy to use, yet very powerful. It will also +automatically enable features like auto-resizing when the system supports it. +""" + +__author__ = 'Nilton Volpato' +__author_email__ = 'first-name dot last-name @ gmail.com' +__date__ = '2011-05-14' +__version__ = '2.3' + +from .compat import * +from .widgets import * +from .progressbar import * diff --git a/deb_dist/multibootusb-8.5.0/scripts/progressbar/compat.py b/deb_dist/multibootusb-8.5.0/scripts/progressbar/compat.py new file mode 100644 index 0000000..a39f4a1 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/progressbar/compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Compatibility methods and classes for the progressbar module.""" + + +# Python 3.x (and backports) use a modified iterator syntax +# This will allow 2.x to behave with 3.x iterators +try: + next +except NameError: + def next(iter): + try: + # Try new style iterators + return iter.__next__() + except AttributeError: + # Fallback in case of a "native" iterator + return iter.next() + + +# Python < 2.5 does not have "any" +try: + any +except NameError: + def any(iterator): + for item in iterator: + if item: return True + return False diff --git a/deb_dist/multibootusb-8.5.0/scripts/progressbar/progressbar.py b/deb_dist/multibootusb-8.5.0/scripts/progressbar/progressbar.py new file mode 100644 index 0000000..7ab79b6 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/progressbar/progressbar.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Main ProgressBar class.""" + +from __future__ import division + +import math +import os +import signal +import sys +import time + +try: + from fcntl import ioctl + from array import array + import termios +except ImportError: + pass + +from .compat import * # for: any, next +from . import widgets + + +class UnknownLength: pass + + +class ProgressBar(object): + """The ProgressBar class which updates and prints the bar. + + A common way of using it is like: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + + You can also use a ProgressBar as an iterator: + >>> progress = ProgressBar() + >>> for i in progress(some_iterable): + ... # do something + ... + + Since the progress bar is incredibly customizable you can specify + different widgets of any type in any order. You can even write your own + widgets! However, since there are already a good number of widgets you + should probably play around with them before moving on to create your own + widgets. + + The term_width parameter represents the current terminal width. If the + parameter is set to an integer then the progress bar will use that, + otherwise it will attempt to determine the terminal width falling back to + 80 columns if the width cannot be determined. + + When implementing a widget's update method you are passed a reference to + the current progress bar. As a result, you have access to the + ProgressBar's methods and attributes. Although there is nothing preventing + you from changing the ProgressBar you should treat it as read only. + + Useful methods and attributes include (Public API): + - currval: current progress (0 <= currval <= maxval) + - maxval: maximum (and final) value + - finished: True if the bar has finished (reached 100%) + - start_time: the time when start() method of ProgressBar was called + - seconds_elapsed: seconds elapsed since start_time and last call to + update + - percentage(): progress in percent [0..100] + """ + + __slots__ = ('currval', 'fd', 'finished', 'last_update_time', + 'left_justify', 'maxval', 'next_update', 'num_intervals', + 'poll', 'seconds_elapsed', 'signal_set', 'start_time', + 'term_width', 'update_interval', 'widgets', '_time_sensitive', + '__iterable') + + _DEFAULT_MAXVAL = 100 + _DEFAULT_TERMSIZE = 80 + _DEFAULT_WIDGETS = [widgets.Percentage(), ' ', widgets.Bar()] + + def __init__(self, maxval=None, widgets=None, term_width=None, poll=1, + left_justify=True, fd=sys.stderr): + """Initializes a progress bar with sane defaults.""" + + # Don't share a reference with any other progress bars + if widgets is None: + widgets = list(self._DEFAULT_WIDGETS) + + self.maxval = maxval + self.widgets = widgets + self.fd = fd + self.left_justify = left_justify + + self.signal_set = False + if term_width is not None: + self.term_width = term_width + else: + try: + self._handle_resize() + signal.signal(signal.SIGWINCH, self._handle_resize) + self.signal_set = True + except (SystemExit, KeyboardInterrupt): raise + except: + self.term_width = self._env_size() + + self.__iterable = None + self._update_widgets() + self.currval = 0 + self.finished = False + self.last_update_time = None + self.poll = poll + self.seconds_elapsed = 0 + self.start_time = None + self.update_interval = 1 + self.next_update = 0 + + + def __call__(self, iterable): + """Use a ProgressBar to iterate through an iterable.""" + + try: + self.maxval = len(iterable) + except: + if self.maxval is None: + self.maxval = UnknownLength + + self.__iterable = iter(iterable) + return self + + + def __iter__(self): + return self + + + def __next__(self): + try: + value = next(self.__iterable) + if self.start_time is None: + self.start() + else: + self.update(self.currval + 1) + return value + except StopIteration: + if self.start_time is None: + self.start() + self.finish() + raise + + + # Create an alias so that Python 2.x won't complain about not being + # an iterator. + next = __next__ + + + def _env_size(self): + """Tries to find the term_width from the environment.""" + + return int(os.environ.get('COLUMNS', self._DEFAULT_TERMSIZE)) - 1 + + + def _handle_resize(self, signum=None, frame=None): + """Tries to catch resize signals sent from the terminal.""" + + h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] + self.term_width = w + + + def percentage(self): + """Returns the progress as a percentage.""" + if self.currval >= self.maxval: + return 100.0 + return (self.currval * 100.0 / self.maxval) if self.maxval else 100.00 + + percent = property(percentage) + + + def _format_widgets(self): + result = [] + expanding = [] + width = self.term_width + + for index, widget in enumerate(self.widgets): + if isinstance(widget, widgets.WidgetHFill): + result.append(widget) + expanding.insert(0, index) + else: + widget = widgets.format_updatable(widget, self) + result.append(widget) + width -= len(widget) + + count = len(expanding) + while count: + portion = max(int(math.ceil(width * 1. / count)), 0) + index = expanding.pop() + count -= 1 + + widget = result[index].update(self, portion) + width -= len(widget) + result[index] = widget + + return result + + + def _format_line(self): + """Joins the widgets and justifies the line.""" + + widgets = ''.join(self._format_widgets()) + + if self.left_justify: return widgets.ljust(self.term_width) + else: return widgets.rjust(self.term_width) + + + def _need_update(self): + """Returns whether the ProgressBar should redraw the line.""" + if self.currval >= self.next_update or self.finished: return True + + delta = time.time() - self.last_update_time + return self._time_sensitive and delta > self.poll + + + def _update_widgets(self): + """Checks all widgets for the time sensitive bit.""" + + self._time_sensitive = any(getattr(w, 'TIME_SENSITIVE', False) + for w in self.widgets) + + + def update(self, value=None): + """Updates the ProgressBar to a new value.""" + + if value is not None and value is not UnknownLength: + if (self.maxval is not UnknownLength + and not 0 <= value <= self.maxval): + + raise ValueError('Value out of range') + + self.currval = value + + + if not self._need_update(): return + if self.start_time is None: + raise RuntimeError('You must call "start" before calling "update"') + + now = time.time() + self.seconds_elapsed = now - self.start_time + self.next_update = self.currval + self.update_interval + self.fd.write(self._format_line() + '\r') + self.fd.flush() + self.last_update_time = now + + + def start(self): + """Starts measuring time, and prints the bar at 0%. + + It returns self so you can use it like this: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + """ + + if self.maxval is None: + self.maxval = self._DEFAULT_MAXVAL + + self.num_intervals = max(100, self.term_width) + self.next_update = 0 + + if self.maxval is not UnknownLength: + if self.maxval < 0: raise ValueError('Value out of range') + self.update_interval = self.maxval / self.num_intervals + + + self.start_time = self.last_update_time = time.time() + self.update(0) + + return self + + + def finish(self): + """Puts the ProgressBar bar in the finished state.""" + + if self.finished: + return + self.finished = True + self.update(self.maxval) + self.fd.write('\n') + if self.signal_set: + signal.signal(signal.SIGWINCH, signal.SIG_DFL) diff --git a/deb_dist/multibootusb-8.5.0/scripts/progressbar/widgets.py b/deb_dist/multibootusb-8.5.0/scripts/progressbar/widgets.py new file mode 100644 index 0000000..6434ad5 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/progressbar/widgets.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Default ProgressBar widgets.""" + +from __future__ import division + +import datetime +import math + +try: + from abc import ABCMeta, abstractmethod +except ImportError: + AbstractWidget = object + abstractmethod = lambda fn: fn +else: + AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) + + +def format_updatable(updatable, pbar): + if hasattr(updatable, 'update'): return updatable.update(pbar) + else: return updatable + + +class Widget(AbstractWidget): + """The base class for all widgets. + + The ProgressBar will call the widget's update value when the widget should + be updated. The widget's size may change between calls, but the widget may + display incorrectly if the size changes drastically and repeatedly. + + The boolean TIME_SENSITIVE informs the ProgressBar that it should be + updated more often because it is time sensitive. + """ + + TIME_SENSITIVE = False + __slots__ = () + + @abstractmethod + def update(self, pbar): + """Updates the widget. + + pbar - a reference to the calling ProgressBar + """ + + +class WidgetHFill(Widget): + """The base class for all variable width widgets. + + This widget is much like the \\hfill command in TeX, it will expand to + fill the line. You can use more than one in the same line, and they will + all have the same width, and together will fill the line. + """ + + @abstractmethod + def update(self, pbar, width): + """Updates the widget providing the total width the widget must fill. + + pbar - a reference to the calling ProgressBar + width - The total width the widget must fill + """ + + +class Timer(Widget): + """Widget which displays the elapsed seconds.""" + + __slots__ = ('format_string',) + TIME_SENSITIVE = True + + def __init__(self, format='Elapsed Time: %s'): + self.format_string = format + + @staticmethod + def format_time(seconds): + """Formats time as the string "HH:MM:SS".""" + + return str(datetime.timedelta(seconds=int(seconds))) + + + def update(self, pbar): + """Updates the widget to show the elapsed time.""" + + return self.format_string % self.format_time(pbar.seconds_elapsed) + + +class ETA(Timer): + """Widget which attempts to estimate the time of arrival.""" + + TIME_SENSITIVE = True + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + eta = elapsed * pbar.maxval / pbar.currval - elapsed + return 'ETA: %s' % self.format_time(eta) + + +class AdaptiveETA(Timer): + """Widget which attempts to estimate the time of arrival. + + Uses a weighted average of two estimates: + 1) ETA based on the total progress and time elapsed so far + 2) ETA based on the progress as per the last 10 update reports + + The weight depends on the current progress so that to begin with the + total progress is used and at the end only the most recent progress is + used. + """ + + TIME_SENSITIVE = True + NUM_SAMPLES = 10 + + def _update_samples(self, currval, elapsed): + sample = (currval, elapsed) + if not hasattr(self, 'samples'): + self.samples = [sample] * (self.NUM_SAMPLES + 1) + else: + self.samples.append(sample) + return self.samples.pop(0) + + def _eta(self, maxval, currval, elapsed): + return elapsed * maxval / float(currval) - elapsed + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) + eta = self._eta(pbar.maxval, pbar.currval, elapsed) + if pbar.currval > currval1: + etasamp = self._eta(pbar.maxval - currval1, + pbar.currval - currval1, + elapsed - elapsed1) + weight = (pbar.currval / float(pbar.maxval)) ** 0.5 + eta = (1 - weight) * eta + weight * etasamp + return 'ETA: %s' % self.format_time(eta) + + +class FileTransferSpeed(Widget): + """Widget for showing the transfer speed (useful for file transfers).""" + + FORMAT = '%6.2f %s%s/s' + PREFIXES = ' kMGTPEZY' + __slots__ = ('unit',) + + def __init__(self, unit='B'): + self.unit = unit + + def update(self, pbar): + """Updates the widget with the current SI prefixed speed.""" + + if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 + scaled = power = 0 + else: + speed = pbar.currval / pbar.seconds_elapsed + power = int(math.log(speed, 1000)) + scaled = speed / 1000.**power + + return self.FORMAT % (scaled, self.PREFIXES[power], self.unit) + + +class AnimatedMarker(Widget): + """An animated marker for the progress bar which defaults to appear as if + it were rotating. + """ + + __slots__ = ('markers', 'curmark') + + def __init__(self, markers='|/-\\'): + self.markers = markers + self.curmark = -1 + + def update(self, pbar): + """Updates the widget to show the next marker or the first marker when + finished""" + + if pbar.finished: return self.markers[0] + + self.curmark = (self.curmark + 1) % len(self.markers) + return self.markers[self.curmark] + +# Alias for backwards compatibility +RotatingMarker = AnimatedMarker + + +class Counter(Widget): + """Displays the current count.""" + + __slots__ = ('format_string',) + + def __init__(self, format='%d'): + self.format_string = format + + def update(self, pbar): + return self.format_string % pbar.currval + + +class Percentage(Widget): + """Displays the current percentage as a number with a percent sign.""" + + def update(self, pbar): + return '%3d%%' % pbar.percentage() + + +class FormatLabel(Timer): + """Displays a formatted label.""" + + mapping = { + 'elapsed': ('seconds_elapsed', Timer.format_time), + 'finished': ('finished', None), + 'last_update': ('last_update_time', None), + 'max': ('maxval', None), + 'seconds': ('seconds_elapsed', None), + 'start': ('start_time', None), + 'value': ('currval', None) + } + + __slots__ = ('format_string',) + def __init__(self, format): + self.format_string = format + + def update(self, pbar): + context = {} + for name, (key, transform) in self.mapping.items(): + try: + value = getattr(pbar, key) + + if transform is None: + context[name] = value + else: + context[name] = transform(value) + except: pass + + return self.format_string % context + + +class SimpleProgress(Widget): + """Returns progress as a count of the total (e.g.: "5 of 47").""" + + __slots__ = ('sep',) + + def __init__(self, sep=' of '): + self.sep = sep + + def update(self, pbar): + return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) + + +class Bar(WidgetHFill): + """A progress bar which stretches to fill the line.""" + + __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=True): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marked, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + # Marked must *always* have length of 1 + if pbar.maxval: + marked *= int(pbar.currval / pbar.maxval * width) + else: + marked = '' + + if self.fill_left: + return '%s%s%s' % (left, marked.ljust(width, self.fill), right) + else: + return '%s%s%s' % (left, marked.rjust(width, self.fill), right) + + +class ReverseBar(Bar): + """A bar which has a marker which bounces from side to side.""" + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=False): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + +class BouncingBar(Bar): + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marker, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + + if pbar.finished: return '%s%s%s' % (left, width * marker, right) + + position = int(pbar.currval % (width * 2 - 1)) + if position > width: position = width * 2 - position + lpad = self.fill * (position - 1) + rpad = self.fill * (width - len(marker) - len(lpad)) + + # Swap if we want to bounce the other way + if not self.fill_left: rpad, lpad = lpad, rpad + + return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/__init__.py new file mode 100644 index 0000000..e757b74 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/__init__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev + ====== + + A binding to libudev. + + The :class:`Context` provides the connection to the udev device database + and enumerates devices. Individual devices are represented by the + :class:`Device` class. + + Device monitoring is provided by :class:`Monitor` and + :class:`MonitorObserver`. With :mod:`pyudev.pyqt4`, :mod:`pyudev.pyside`, + :mod:`pyudev.glib` and :mod:`pyudev.wx` device monitoring can be integrated + into the event loop of various GUI toolkits. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ._errors import DeviceNotFoundAtPathError +from ._errors import DeviceNotFoundByFileError +from ._errors import DeviceNotFoundByNameError +from ._errors import DeviceNotFoundByNumberError +from ._errors import DeviceNotFoundError +from ._errors import DeviceNotFoundInEnvironmentError + +from .device import Attributes +from .device import Device +from .device import Devices +from .device import Tags + +from .discover import DeviceFileHypothesis +from .discover import DeviceNameHypothesis +from .discover import DeviceNumberHypothesis +from .discover import DevicePathHypothesis +from .discover import Discovery + +from .core import Context +from .core import Enumerator + +from .monitor import Monitor +from .monitor import MonitorObserver + +from .version import __version__ +from .version import __version_info__ + +from ._util import udev_version diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_compat.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_compat.py new file mode 100644 index 0000000..f21eb98 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._compat + ============== + + Compatibility for Python versions, that lack certain functions. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from subprocess import Popen, CalledProcessError, PIPE + + +def check_output(command): + """ + Compatibility with :func:`subprocess.check_output` from Python 2.7 and + upwards. + """ + proc = Popen(command, stdout=PIPE) + output = proc.communicate()[0] + if proc.returncode != 0: + raise CalledProcessError(proc.returncode, command) + return output diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py new file mode 100644 index 0000000..cb5ed88 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib + ================= + + Wrappers for libraries. + + .. moduleauthor:: mulhern +""" + +from . import libc +from . import libudev diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py new file mode 100644 index 0000000..f4de520 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib._errorcheckers + ================================ + + Error checkers for ctypes wrappers. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + + +import os +import errno +from ctypes import get_errno + + +ERRNO_EXCEPTIONS = { + errno.ENOMEM: MemoryError, + errno.EOVERFLOW: OverflowError, + errno.EINVAL: ValueError +} + + +def exception_from_errno(errnum): + """Create an exception from ``errnum``. + + ``errnum`` is an integral error number. + + Return an exception object appropriate to ``errnum``. + + """ + exception = ERRNO_EXCEPTIONS.get(errnum) + errorstr = os.strerror(errnum) + if exception is not None: + return exception(errorstr) + else: + return EnvironmentError(errnum, errorstr) + + +def check_negative_errorcode(result, func, *args): + """Error checker for funtions, which return negative error codes. + + If ``result`` is smaller than ``0``, it is interpreted as negative error + code, and an appropriate exception is raised: + + - ``-ENOMEM`` raises a :exc:`~exceptions.MemoryError` + - ``-EOVERFLOW`` raises a :exc:`~exceptions.OverflowError` + - all other error codes raise :exc:`~exceptions.EnvironmentError` + + If result is greater or equal to ``0``, it is returned unchanged. + + """ + if result < 0: + # udev returns the *negative* errno code at this point + errnum = -result + raise exception_from_errno(errnum) + else: + return result + + +def check_errno_on_nonzero_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is not ``0``, an exception according to this errno is raised. + Otherwise nothing happens. + + """ + if result != 0: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result + + +def check_errno_on_null_pointer_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is a null pointer, an exception according to this errno is + raised. Otherwise nothing happens. + + """ + # pylint: disable=invalid-name + if not result: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py new file mode 100644 index 0000000..112c73b --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.libc + ====================== + + Wrappers for libc. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_int + +from ._errorcheckers import check_errno_on_nonzero_return + + +fd_pair = c_int * 2 + + +SIGNATURES = dict( + pipe2=([fd_pair, c_int], c_int), +) + +ERROR_CHECKERS = dict( + pipe2=check_errno_on_nonzero_return, +) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py new file mode 100644 index 0000000..901b124 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._ctypeslib.libudev + ========================= + + Wrapper types for libudev. Use ``libudev`` attribute to access libudev + functions. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_uint +from ctypes import c_ulonglong +from ctypes import CDLL +from ctypes import Structure +from ctypes import POINTER + +from ctypes.util import find_library + +from ._errorcheckers import check_errno_on_nonzero_return +from ._errorcheckers import check_errno_on_null_pointer_return +from ._errorcheckers import check_negative_errorcode + + +class udev(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_p = POINTER(udev) # pylint: disable=invalid-name + + +class udev_enumerate(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_enumerate`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_enumerate_p = POINTER(udev_enumerate) # pylint: disable=invalid-name + + +class udev_list_entry(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_list_entry`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_list_entry_p = POINTER(udev_list_entry) # pylint: disable=invalid-name + + +class udev_device(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_device_p = POINTER(udev_device) # pylint: disable=invalid-name + + +class udev_monitor(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_monitor_p = POINTER(udev_monitor) # pylint: disable=invalid-name + +class udev_hwdb(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_hwdb`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_hwdb_p = POINTER(udev_hwdb) # pylint: disable=invalid-name + + +dev_t = c_ulonglong # pylint: disable=invalid-name + + +SIGNATURES = dict( + # context + udev_new=([], udev_p), + udev_unref=([udev_p], None), + udev_ref=([udev_p], udev_p), + udev_get_sys_path=([udev_p], c_char_p), + udev_get_dev_path=([udev_p], c_char_p), + udev_get_run_path=([udev_p], c_char_p), + udev_get_log_priority=([udev_p], c_int), + udev_set_log_priority=([udev_p, c_int], None), + udev_enumerate_new=([udev_p], udev_enumerate_p), + udev_enumerate_ref=([udev_enumerate_p], udev_enumerate_p), + udev_enumerate_unref=([udev_enumerate_p], None), + udev_enumerate_add_match_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_nomatch_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_property=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_nomatch_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_tag=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_sysname=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_parent=([udev_enumerate_p, udev_device_p], c_int), + udev_enumerate_add_match_is_initialized=([udev_enumerate_p], c_int), + udev_enumerate_scan_devices=([udev_enumerate_p], c_int), + udev_enumerate_get_list_entry=([udev_enumerate_p], udev_list_entry_p), + # list entries + udev_list_entry_get_next=([udev_list_entry_p], udev_list_entry_p), + udev_list_entry_get_name=([udev_list_entry_p], c_char_p), + udev_list_entry_get_value=([udev_list_entry_p], c_char_p), + # devices + udev_device_ref=([udev_device_p], udev_device_p), + udev_device_unref=([udev_device_p], None), + udev_device_new_from_syspath=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_subsystem_sysname=( + [udev_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_new_from_devnum=([udev_p, c_char, dev_t], udev_device_p), + udev_device_new_from_device_id=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_environment=([udev_p], udev_device_p), + udev_device_get_parent=([udev_device_p], udev_device_p), + udev_device_get_parent_with_subsystem_devtype=( + [udev_device_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_get_devpath=([udev_device_p], c_char_p), + udev_device_get_subsystem=([udev_device_p], c_char_p), + udev_device_get_syspath=([udev_device_p], c_char_p), + udev_device_get_sysnum=([udev_device_p], c_char_p), + udev_device_get_sysname=([udev_device_p], c_char_p), + udev_device_get_driver=([udev_device_p], c_char_p), + udev_device_get_devtype=([udev_device_p], c_char_p), + udev_device_get_devnode=([udev_device_p], c_char_p), + udev_device_get_property_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_sysattr_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_devnum=([udev_device_p], dev_t), + udev_device_get_action=([udev_device_p], c_char_p), + udev_device_get_seqnum=([udev_device_p], c_ulonglong), + udev_device_get_is_initialized=([udev_device_p], c_int), + udev_device_get_usec_since_initialized=([udev_device_p], c_ulonglong), + udev_device_get_devlinks_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_tags_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_properties_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_sysattr_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_set_sysattr_value=([udev_device_p, c_char_p, c_char_p], c_int), + udev_device_has_tag=([udev_device_p, c_char_p], c_int), + # monitoring + udev_monitor_ref=([udev_monitor_p], udev_monitor_p), + udev_monitor_unref=([udev_monitor_p], None), + udev_monitor_new_from_netlink=([udev_p, c_char_p], udev_monitor_p), + udev_monitor_enable_receiving=([udev_monitor_p], c_int), + udev_monitor_set_receive_buffer_size=([udev_monitor_p, c_int], c_int), + udev_monitor_get_fd=([udev_monitor_p], c_int), + udev_monitor_receive_device=([udev_monitor_p], udev_device_p), + udev_monitor_filter_add_match_subsystem_devtype=( + [udev_monitor_p, c_char_p, c_char_p], c_int), + udev_monitor_filter_add_match_tag=([udev_monitor_p, c_char_p], c_int), + udev_monitor_filter_update=([udev_monitor_p], c_int), + udev_monitor_filter_remove=([udev_monitor_p], c_int), + # hwdb + udev_hwdb_ref=([udev_hwdb_p], udev_hwdb_p), + udev_hwdb_unref=([udev_hwdb_p], None), + udev_hwdb_new=([udev_p], udev_hwdb_p), + udev_hwdb_get_properties_list_entry=( + [udev_hwdb_p, c_char_p, c_uint], + udev_list_entry_p + ) +) + + +ERROR_CHECKERS = dict( + udev_device_get_action=None, + udev_device_get_devlinks_list_entry=None, + udev_device_get_devnode=None, + udev_device_get_devnum=None, + udev_device_get_devpath=None, + udev_device_get_devtype=None, + udev_device_get_driver=None, + udev_device_get_is_initialized=None, + udev_device_get_parent=None, + udev_device_get_parent_with_subsystem_devtype=None, + udev_device_get_properties_list_entry=None, + udev_device_get_property_value=None, + udev_device_get_seqnum=None, + udev_device_get_subsystem=None, + udev_device_get_sysattr_list_entry=None, + udev_device_get_sysattr_value=None, + udev_device_get_sysname=None, + udev_device_get_sysnum=None, + udev_device_get_syspath=None, + udev_device_get_tags_list_entry=None, + udev_device_get_usec_since_initialized=None, + udev_device_has_tag=None, + udev_device_new_from_device_id=None, + udev_device_new_from_devnum=None, + udev_device_new_from_environment=None, + udev_device_new_from_subsystem_sysname=None, + udev_device_new_from_syspath=None, + udev_device_ref=None, + udev_device_unref=None, + udev_device_set_sysattr_value=check_negative_errorcode, + udev_enumerate_add_match_parent=check_negative_errorcode, + udev_enumerate_add_match_subsystem=check_negative_errorcode, + udev_enumerate_add_nomatch_subsystem=check_negative_errorcode, + udev_enumerate_add_match_property=check_negative_errorcode, + udev_enumerate_add_match_sysattr=check_negative_errorcode, + udev_enumerate_add_nomatch_sysattr=check_negative_errorcode, + udev_enumerate_add_match_tag=check_negative_errorcode, + udev_enumerate_add_match_sysname=check_negative_errorcode, + udev_enumerate_add_match_is_initialized=check_negative_errorcode, + udev_enumerate_get_list_entry=None, + udev_enumerate_new=None, + udev_enumerate_ref=None, + udev_enumerate_scan_devices=None, + udev_enumerate_unref=None, + udev_get_dev_path=None, + udev_get_log_priority=None, + udev_get_run_path=None, + udev_get_sys_path=None, + udev_hwdb_get_properties_list_entry=None, + udev_hwdb_new=None, + udev_hwdb_ref=None, + udev_hwdb_unref=None, + udev_list_entry_get_name=None, + udev_list_entry_get_next=None, + udev_list_entry_get_value=None, + udev_monitor_set_receive_buffer_size=check_errno_on_nonzero_return, + # libudev doc says, enable_receiving returns a negative errno, but tests + # show that this is not reliable, so query the real error code + udev_monitor_enable_receiving=check_errno_on_nonzero_return, + udev_monitor_receive_device=check_errno_on_null_pointer_return, + udev_monitor_ref=None, + udev_monitor_filter_add_match_subsystem_devtype=check_negative_errorcode, + udev_monitor_filter_add_match_tag=check_negative_errorcode, + udev_monitor_filter_update=check_errno_on_nonzero_return, + udev_monitor_filter_remove=check_errno_on_nonzero_return, + udev_monitor_get_fd=None, + udev_monitor_new_from_netlink=None, + udev_monitor_unref=None, + udev_new=None, + udev_ref=None, + udev_set_log_priority=None, + udev_unref=None +) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py new file mode 100644 index 0000000..d10b6c9 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.utils + ======================= + + Utilities for loading ctypeslib. + + .. moduleauthor:: Anne Mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import CDLL +from ctypes.util import find_library + + +def load_ctypes_library(name, signatures, error_checkers): + """ + Load library ``name`` and return a :class:`ctypes.CDLL` object for it. + + :param str name: the library name + :param signatures: signatures of methods + :type signatures: dict of str * (tuple of (list of type) * type) + :param error_checkers: error checkers for methods + :type error_checkers: dict of str * ((int * ptr * arglist) -> int) + + The library has errno handling enabled. + Important functions are given proper signatures and return types to support + type checking and argument conversion. + + :returns: a loaded library + :rtype: ctypes.CDLL + :raises ImportError: if the library is not found + """ + library_name = find_library(name) + if not library_name: + raise ImportError('No library named %s' % name) + lib = CDLL(library_name, use_errno=True) + # Add function signatures + for funcname, signature in signatures.items(): + function = getattr(lib, funcname, None) + if function: + argtypes, restype = signature + function.argtypes = argtypes + function.restype = restype + errorchecker = error_checkers.get(funcname) + if errorchecker: + function.errcheck = errorchecker + return lib diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_errors.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_errors.py new file mode 100644 index 0000000..847d908 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_errors.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device._errors + ===================== + + Errors raised by Device methods. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc + +from six import add_metaclass + +@add_metaclass(abc.ABCMeta) +class DeviceError(Exception): + """ + Any error raised when messing around w/ or trying to discover devices. + """ + + +@add_metaclass(abc.ABCMeta) +class DeviceNotFoundError(DeviceError): + """ + An exception indicating that no :class:`Device` was found. + + .. versionchanged:: 0.5 + Rename from ``NoSuchDeviceError`` to its current name. + """ + + +class DeviceNotFoundAtPathError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found at a given path. + """ + + def __init__(self, sys_path): + DeviceNotFoundError.__init__(self, sys_path) + + @property + def sys_path(self): + """ + The path that caused this error as string. + """ + return self.args[0] + + def __str__(self): + return 'No device at {0!r}'.format(self.sys_path) + + +class DeviceNotFoundByFileError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found from the given filename. + """ + +class DeviceNotFoundByInterfaceIndexError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given interface index. + """ + +class DeviceNotFoundByKernelDeviceError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given kernel device string. + + The format of the kernel device string is defined in the + systemd.journal-fields man pages. + """ + + +class DeviceNotFoundByNameError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found with a given name. + """ + + def __init__(self, subsystem, sys_name): + DeviceNotFoundError.__init__(self, subsystem, sys_name) + + @property + def subsystem(self): + """ + The subsystem that caused this error as string. + """ + return self.args[0] + + @property + def sys_name(self): + """ + The sys name that caused this error as string. + """ + return self.args[1] + + def __str__(self): + return 'No device {0.sys_name!r} in {0.subsystem!r}'.format(self) + + +class DeviceNotFoundByNumberError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` was found + for a given device number. + """ + + def __init__(self, typ, number): + DeviceNotFoundError.__init__(self, typ, number) + + @property + def device_type(self): + """ + The device type causing this error as string. Either ``'char'`` or + ``'block'``. + """ + return self.args[0] + + @property + def device_number(self): + """ + The device number causing this error as integer. + """ + return self.args[1] + + def __str__(self): + return ('No {0.device_type} device with number ' + '{0.device_number}'.format(self)) + + +class DeviceNotFoundInEnvironmentError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` could + be constructed from the process environment. + """ + + def __str__(self): + return 'No device found in environment' + + +class DeviceValueError(DeviceError): + """ + Raised when a parameter has an unacceptable value. + + May also be raised when the parameter has an unacceptable type. + """ + + _FMT_STR = "value '%s' for parameter %s is unacceptable" + + def __init__(self, value, param, msg=None): + """ Initializer. + + :param object value: the value + :param str param: the parameter + :param str msg: an explanatory message + """ + # pylint: disable=super-init-not-called + self._value = value + self._param = param + self._msg = msg + + def __str__(self): + if self._msg: + fmt_str = self._FMT_STR + ": %s" + return fmt_str % (self._value, self._param, self._msg) + else: + return self._FMT_STR % (self._value, self._param) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py new file mode 100644 index 0000000..3b434ba --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._os + ========== + + Extras to compensate for deficiencies in python os module. + + .. moduleauthor:: mulhern +""" + +from . import pipe +from . import poll diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py new file mode 100644 index 0000000..41890fb --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.pipe + =============== + + Fallback implementations for pipe. + + 1. pipe2 from python os module + 2. pipe2 from libc + 3. pipe from python os module + + The Pipe class wraps the chosen implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import fcntl +from functools import partial + +from .._ctypeslib.libc import fd_pair +from .._ctypeslib.libc import ERROR_CHECKERS +from .._ctypeslib.libc import SIGNATURES +from .._ctypeslib.utils import load_ctypes_library + +# Define O_CLOEXEC, if not present in os already +O_CLOEXEC = getattr(os, 'O_CLOEXEC', 0o2000000) + + +def _pipe2_ctypes(libc, flags): + """A ``pipe2`` implementation using ``pipe2`` from ctypes. + + ``libc`` is a :class:`ctypes.CDLL` object for libc. ``flags`` is an + integer providing the flags to ``pipe2``. + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = fd_pair() + libc.pipe2(fds, flags) + return fds[0], fds[1] + + +def _pipe2_by_pipe(flags): + """A ``pipe2`` implementation using :func:`os.pipe`. + + ``flags`` is an integer providing the flags to ``pipe2``. + + .. warning:: + + This implementation is not atomic! + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = os.pipe() + if flags & os.O_NONBLOCK != 0: + for fd in fds: + set_fd_status_flag(fd, os.O_NONBLOCK) + if flags & O_CLOEXEC != 0: + for fd in fds: + set_fd_flag(fd, O_CLOEXEC) + return fds + + +def _get_pipe2_implementation(): + """Find the appropriate implementation for ``pipe2``. + +Return a function implementing ``pipe2``.""" + if hasattr(os, 'pipe2'): + return os.pipe2 # pylint: disable=no-member + else: + try: + libc = load_ctypes_library("libc", SIGNATURES, ERROR_CHECKERS) + return (partial(_pipe2_ctypes, libc) + if hasattr(libc, 'pipe2') else + _pipe2_by_pipe) + except ImportError: + return _pipe2_by_pipe + + +_PIPE2 = _get_pipe2_implementation() + + +def set_fd_flag(fd, flag): + """Set a flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFD, 0) + fcntl.fcntl(fd, fcntl.F_SETFD, flags | flag) + + +def set_fd_status_flag(fd, flag): + """Set a status flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | flag) + + +class Pipe(object): + """A unix pipe. + + A pipe object provides two file objects: :attr:`source` is a readable file + object, and :attr:`sink` a writeable. Bytes written to :attr:`sink` appear + at :attr:`source`. + + Open a pipe with :meth:`open()`. + + """ + + @classmethod + def open(cls): + """Open and return a new :class:`Pipe`. + + The pipe uses non-blocking IO.""" + source, sink = _PIPE2(os.O_NONBLOCK | O_CLOEXEC) + return cls(source, sink) + + def __init__(self, source_fd, sink_fd): + """Create a new pipe object from the given file descriptors. + + ``source_fd`` is a file descriptor for the readable side of the pipe, + ``sink_fd`` is a file descriptor for the writeable side.""" + self.source = os.fdopen(source_fd, 'rb', 0) + self.sink = os.fdopen(sink_fd, 'wb', 0) + + def close(self): + """Closes both sides of the pipe.""" + try: + self.source.close() + finally: + self.sink.close() diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/poll.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/poll.py new file mode 100644 index 0000000..e760a36 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_os/poll.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.poll + =============== + + Operating system interface for pyudev. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import select + +from .._util import eintr_retry_call + + +class Poll(object): + """A poll object. + + This object essentially provides a more convenient interface around + :class:`select.poll`. + + """ + + _EVENT_TO_MASK = {'r': select.POLLIN, + 'w': select.POLLOUT} + + @staticmethod + def _has_event(events, event): + return events & event != 0 + + @classmethod + def for_events(cls, *events): + """Listen for ``events``. + + ``events`` is a list of ``(fd, event)`` pairs, where ``fd`` is a file + descriptor or file object and ``event`` either ``'r'`` or ``'w'``. If + ``r``, listen for whether that is ready to be read. If ``w``, listen + for whether the channel is ready to be written to. + + """ + notifier = eintr_retry_call(select.poll) + for fd, event in events: + mask = cls._EVENT_TO_MASK.get(event) + if not mask: + raise ValueError('Unknown event type: {0!r}'.format(event)) + notifier.register(fd, mask) + return cls(notifier) + + def __init__(self, notifier): + """Create a poll object for the given ``notifier``. + + ``notifier`` is the :class:`select.poll` object wrapped by the new poll + object. + + """ + self._notifier = notifier + + def poll(self, timeout=None): + """Poll for events. + + ``timeout`` is an integer specifying how long to wait for events (in + milliseconds). If omitted, ``None`` or negative, wait until an event + occurs. + + Return a list of all events that occurred before ``timeout``, where + each event is a pair ``(fd, event)``. ``fd`` is the integral file + descriptor, and ``event`` a string indicating the event type. If + ``'r'``, there is data to read from ``fd``. If ``'w'``, ``fd`` is + writable without blocking now. If ``'h'``, the file descriptor was + hung up (i.e. the remote side of a pipe was closed). + + """ + # Return a list to allow clients to determine whether there are any + # events at all with a simple truthiness test. + return list(self._parse_events(eintr_retry_call(self._notifier.poll, timeout))) + + def _parse_events(self, events): + """Parse ``events``. + + ``events`` is a list of events as returned by + :meth:`select.poll.poll()`. + + Yield all parsed events. + + """ + for fd, event_mask in events: + if self._has_event(event_mask, select.POLLNVAL): + raise IOError('File descriptor not open: {0!r}'.format(fd)) + elif self._has_event(event_mask, select.POLLERR): + raise IOError('Error while polling fd: {0!r}'.format(fd)) + + if self._has_event(event_mask, select.POLLIN): + yield fd, 'r' + if self._has_event(event_mask, select.POLLOUT): + yield fd, 'w' + if self._has_event(event_mask, select.POLLHUP): + yield fd, 'h' diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_qt_base.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_qt_base.py new file mode 100644 index 0000000..dd227f2 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_qt_base.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._qt_base + =============== + + Base mixin class for Qt4,Qt5 support. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six + +from pyudev.device import Device + +class MonitorObserverMixin(object): + """ + Base mixin for pyqt monitor observers. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + self.monitor = monitor + self.notifier = notifier_class( + monitor.fileno(), notifier_class.Read, self) + self.notifier.activated[int].connect(self._process_udev_event) + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. This merely + reflects the state of the ``enabled`` property of the underlying + :attr:`notifier`. + + .. versionadded:: 0.14 + """ + return self.notifier.isEnabled() + + @enabled.setter + def enabled(self, value): + self.notifier.setEnabled(value) + + def _process_udev_event(self): + """ + Attempt to receive a single device event from the monitor, process + the event and emit corresponding signals. + + Called by ``QSocketNotifier``, if data is available on the udev + monitoring socket. + """ + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + + def _emit_event(self, device): + self.deviceEvent.emit(device) + + +class QUDevMonitorObserverMixin(MonitorObserverMixin): + """ + Obsolete monitor observer mixin. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + MonitorObserverMixin._setup_notifier(self, monitor, notifier_class) + self._action_signal_map = { + 'add': self.deviceAdded, 'remove': self.deviceRemoved, + 'change': self.deviceChanged, 'move': self.deviceMoved, + } + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.pyqt4.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.deviceEvent.emit(device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + signal.emit(device) + +def make_init(qobject, socket_notifier): + """ + Generates an initializer to observer the given ``monitor`` + (a :class:`~pyudev.Monitor`): + + ``parent`` is the parent :class:`~PyQt{4,5}.QtCore.QObject` of this + object. It is passed unchanged to the inherited constructor of + :class:`~PyQt{4,5}.QtCore.QObject`. + """ + + def __init__(self, monitor, parent=None): + qobject.__init__(self, parent) + # pylint: disable=protected-access + self._setup_notifier(monitor, socket_notifier) + + return __init__ + + +class MonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PySide, PyQt{4,5}}.QtCore.QObject`. + + """ + return type( + str("MonitorObserver"), + (qobject, MonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + str("deviceEvent") : signal(Device) + } + ) + + + +class QUDevMonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PyQt{4,5}, PySide}.QtCore.QObject`. + + """ + return type( + str("QUDevMonitorObserver"), + (qobject, QUDevMonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + #: emitted upon arbitrary device events + str("deviceEvent") : signal(six.text_type, Device), + #: emitted if a device was added + str("deviceAdded") : signal(Device), + #: emitted if a device was removed + str("deviceRemoved") : signal(Device), + #: emitted if a device was changed + str("deviceChanged") : signal(Device), + #: emitted if a device was moved + str("deviceMoved") : signal(Device) + } + ) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/_util.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_util.py new file mode 100644 index 0000000..d6d736c --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/_util.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._util + ============ + + Internal utilities + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +try: + from subprocess import check_output +except ImportError: + from pyudev._compat import check_output + +import os +import sys +import stat +import errno + +import six + + +def ensure_byte_string(value): + """ + Return the given ``value`` as bytestring. + + If the given ``value`` is not a byte string, but a real unicode string, it + is encoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, bytes): + value = value.encode(sys.getfilesystemencoding()) + return value + + +def ensure_unicode_string(value): + """ + Return the given ``value`` as unicode string. + + If the given ``value`` is not a unicode string, but a byte string, it is + decoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, six.text_type): + value = value.decode(sys.getfilesystemencoding()) + return value + + +def property_value_to_bytes(value): + """ + Return a byte string, which represents the given ``value`` in a way + suitable as raw value of an udev property. + + If ``value`` is a boolean object, it is converted to ``'1'`` or ``'0'``, + depending on whether ``value`` is ``True`` or ``False``. If ``value`` is a + byte string already, it is returned unchanged. Anything else is simply + converted to a unicode string, and then passed to + :func:`ensure_byte_string`. + """ + # udev represents boolean values as 1 or 0, therefore an explicit + # conversion to int is required for boolean values + if isinstance(value, bool): + value = int(value) + if isinstance(value, bytes): + return value + else: + return ensure_byte_string(six.text_type(value)) + + +def string_to_bool(value): + """ + Convert the given unicode string ``value`` to a boolean object. + + If ``value`` is ``'1'``, ``True`` is returned. If ``value`` is ``'0'``, + ``False`` is returned. Any other value raises a + :exc:`~exceptions.ValueError`. + """ + if value not in ('1', '0'): + raise ValueError('Not a boolean value: {0!r}'.format(value)) + return value == '1' + + +def udev_list_iterate(libudev, entry): + """ + Iteration helper for udev list entry objects. + + Yield a tuple ``(name, value)``. ``name`` and ``value`` are bytestrings + containing the name and the value of the list entry. The exact contents + depend on the list iterated over. + """ + while entry: + name = libudev.udev_list_entry_get_name(entry) + value = libudev.udev_list_entry_get_value(entry) + yield (name, value) + entry = libudev.udev_list_entry_get_next(entry) + + +def get_device_type(filename): + """ + Get the device type of a device file. + + ``filename`` is a string containing the path of a device file. + + Return ``'char'`` if ``filename`` is a character device, or ``'block'`` if + ``filename`` is a block device. Raise :exc:`~exceptions.ValueError` if + ``filename`` is no device file at all. Raise + :exc:`~exceptions.EnvironmentError` if ``filename`` does not exist or if + its metadata was inaccessible. + + .. versionadded:: 0.15 + """ + mode = os.stat(filename).st_mode + if stat.S_ISCHR(mode): + return 'char' + elif stat.S_ISBLK(mode): + return 'block' + else: + raise ValueError('not a device file: {0!r}'.format(filename)) + + +def eintr_retry_call(func, *args, **kwargs): + """ + Handle interruptions to an interruptible system call. + + Run an interruptible system call in a loop and retry if it raises EINTR. + The signal calls that may raise EINTR prior to Python 3.5 are listed in + PEP 0475. Any calls to these functions must be wrapped in eintr_retry_call + in order to handle EINTR returns in older versions of Python. + + This function is safe to use under Python 3.5 and newer since the wrapped + function will simply return without raising EINTR. + + This function is based on _eintr_retry_call in python's subprocess.py. + """ + + # select.error inherits from Exception instead of OSError in Python 2 + import select + + while True: + try: + return func(*args, **kwargs) + except (OSError, IOError, select.error) as err: + # If this is not an IOError or OSError, it's the old select.error + # type, which means that the errno is only accessible via subscript + if isinstance(err, (OSError, IOError)): + error_code = err.errno + else: + error_code = err.args[0] + + if error_code == errno.EINTR: + continue + raise + +def udev_version(): + """ + Get the version of the underlying udev library. + + udev doesn't use a standard major-minor versioning scheme, but instead + labels releases with a single consecutive number. Consequently, the + version number returned by this function is a single integer, and not a + tuple (like for instance the interpreter version in + :data:`sys.version_info`). + + As libudev itself does not provide a function to query the version number, + this function calls the ``udevadm`` utility, so be prepared to catch + :exc:`~exceptions.EnvironmentError` and + :exc:`~subprocess.CalledProcessError` if you call this function. + + Return the version number as single integer. Raise + :exc:`~exceptions.ValueError`, if the version number retrieved from udev + could not be converted to an integer. Raise + :exc:`~exceptions.EnvironmentError`, if ``udevadm`` was not found, or could + not be executed. Raise :exc:`subprocess.CalledProcessError`, if + ``udevadm`` returned a non-zero exit code. On Python 2.7 or newer, the + ``output`` attribute of this exception is correctly set. + + .. versionadded:: 0.8 + """ + output = ensure_unicode_string(check_output(['udevadm', '--version'])) + return int(output.strip()) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/core.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/core.py new file mode 100644 index 0000000..9cb6dbe --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/core.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.core + =========== + + Core types and functions of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from .device import Devices +from ._errors import DeviceNotFoundAtPathError +from ._ctypeslib.libudev import ERROR_CHECKERS +from ._ctypeslib.libudev import SIGNATURES +from ._ctypeslib.utils import load_ctypes_library + +from ._util import ensure_byte_string +from ._util import ensure_unicode_string +from ._util import property_value_to_bytes +from ._util import udev_list_iterate + + +class Context(object): + """ + A device database connection. + + This class represents a connection to the udev device database, and is + really *the* central object to access udev. You need an instance of this + class for almost anything else in pyudev. + + This class itself gives access to various udev configuration data (e.g. + :attr:`sys_path`, :attr:`device_path`), and provides device enumeration + (:meth:`list_devices()`). + + Instances of this class can directly be given as ``udev *`` to functions + wrapped through :mod:`ctypes`. + """ + + def __init__(self): + """ + Create a new context. + """ + self._libudev = load_ctypes_library('udev', SIGNATURES, ERROR_CHECKERS) + self._as_parameter_ = self._libudev.udev_new() + + def __del__(self): + self._libudev.udev_unref(self) + + @property + def sys_path(self): + """ + The ``sysfs`` mount point defaulting to ``/sys'`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_sys_path'): + return ensure_unicode_string(self._libudev.udev_get_sys_path(self)) + else: + # Fixed path since udev 183 + return '/sys' + + @property + def device_path(self): + """ + The device directory path defaulting to ``/dev`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_dev_path'): + return ensure_unicode_string(self._libudev.udev_get_dev_path(self)) + else: + # Fixed path since udev 183 + return '/dev' + + @property + def run_path(self): + """ + The run runtime directory path defaulting to ``/run`` as unicode + string. + + .. udevversion:: 167 + + .. versionadded:: 0.10 + """ + if hasattr(self._libudev, 'udev_get_run_path'): + return ensure_unicode_string(self._libudev.udev_get_run_path(self)) + else: + return '/run/udev' + + @property + def log_priority(self): + """ + The logging priority of the interal logging facitility of udev as + integer with a standard :mod:`syslog` priority. Assign to this + property to change the logging priority. + + UDev uses the standard :mod:`syslog` priorities. Constants for these + priorities are defined in the :mod:`syslog` module in the standard + library: + + >>> import syslog + >>> context = pyudev.Context() + >>> context.log_priority = syslog.LOG_DEBUG + + .. versionadded:: 0.9 + """ + return self._libudev.udev_get_log_priority(self) + + @log_priority.setter + def log_priority(self, value): + """ + Set the log priority. + + :param int value: the log priority. + """ + self._libudev.udev_set_log_priority(self, value) + + def list_devices(self, **kwargs): + """ + List all available devices. + + The arguments of this method are the same as for + :meth:`Enumerator.match()`. In fact, the arguments are simply passed + straight to method :meth:`~Enumerator.match()`. + + This function creates and returns an :class:`Enumerator` object, + that can be used to filter the list of devices, and eventually + retrieve :class:`Device` objects representing matching devices. + + .. versionchanged:: 0.8 + Accept keyword arguments now for easy matching. + """ + return Enumerator(self).match(**kwargs) + + +class Enumerator(object): + """ + A filtered iterable of devices. + + To retrieve devices, simply iterate over an instance of this class. + This operation yields :class:`Device` objects representing the available + devices. + + Before iteration the device list can be filtered by subsystem or by + property values using :meth:`match_subsystem` and + :meth:`match_property`. Multiple subsystem (property) filters are + combined using a logical OR, filters of different types are combined + using a logical AND. The following filter for instance:: + + devices.match_subsystem('block').match_property( + 'ID_TYPE', 'disk').match_property('DEVTYPE', 'disk') + + means the following:: + + subsystem == 'block' and (ID_TYPE == 'disk' or DEVTYPE == 'disk') + + Once added, a filter cannot be removed anymore. Create a new object + instead. + + Instances of this class can directly be given as given ``udev_enumerate *`` + to functions wrapped through :mod:`ctypes`. + """ + + def __init__(self, context): + """ + Create a new enumerator with the given ``context`` (a + :class:`Context` instance). + + While you can create objects of this class directly, this is not + recommended. Call :method:`Context.list_devices()` instead. + """ + if not isinstance(context, Context): + raise TypeError('Invalid context object') + self.context = context + self._as_parameter_ = context._libudev.udev_enumerate_new(context) + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_enumerate_unref(self) + + def match(self, **kwargs): + """ + Include devices according to the rules defined by the keyword + arguments. These keyword arguments are interpreted as follows: + + - The value for the keyword argument ``subsystem`` is forwarded to + :meth:`match_subsystem()`. + - The value for the keyword argument ``sys_name`` is forwared to + :meth:`match_sys_name()`. + - The value for the keyword argument ``tag`` is forwared to + :meth:`match_tag()`. + - The value for the keyword argument ``parent`` is forwared to + :meth:`match_parent()`. + - All other keyword arguments are forwareded one by one to + :meth:`match_property()`. The keyword argument itself is interpreted + as property name, the value of the keyword argument as the property + value. + + All keyword arguments are optional, calling this method without no + arguments at all is simply a noop. + + Return the instance again. + + .. versionadded:: 0.8 + + .. versionchanged:: 0.13 + Add ``parent`` keyword. + """ + subsystem = kwargs.pop('subsystem', None) + if subsystem is not None: + self.match_subsystem(subsystem) + sys_name = kwargs.pop('sys_name', None) + if sys_name is not None: + self.match_sys_name(sys_name) + tag = kwargs.pop('tag', None) + if tag is not None: + self.match_tag(tag) + parent = kwargs.pop('parent', None) + if parent is not None: + self.match_parent(parent) + for prop, value in kwargs.items(): + self.match_property(prop, value) + return self + + def match_subsystem(self, subsystem, nomatch=False): + """ + Include all devices, which are part of the given ``subsystem``. + + ``subsystem`` is either a unicode string or a byte string, containing + the name of the subsystem. If ``nomatch`` is ``True`` (default is + ``False``), the match is inverted: A device is only included if it is + *not* part of the given ``subsystem``. + + Note that, if a device has no subsystem, it is not included either + with value of ``nomatch`` True or with value of ``nomatch`` False. + + Return the instance again. + """ + match = self._libudev.udev_enumerate_add_nomatch_subsystem \ + if nomatch else self._libudev.udev_enumerate_add_match_subsystem + match(self, ensure_byte_string(subsystem)) + return self + + def match_sys_name(self, sys_name): + """ + Include all devices with the given name. + + ``sys_name`` is a byte or unicode string containing the device name. + + Return the instance again. + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_sysname( + self, ensure_byte_string(sys_name)) + return self + + def match_property(self, prop, value): + """ + Include all devices, whose ``prop`` has the given ``value``. + + ``prop`` is either a unicode string or a byte string, containing + the name of the property to match. ``value`` is a property value, + being one of the following types: + + - :func:`int` + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + Return the instance again. + """ + self._libudev.udev_enumerate_add_match_property( + self, ensure_byte_string(prop), property_value_to_bytes(value)) + return self + + def match_attribute(self, attribute, value, nomatch=False): + """ + Include all devices, whose ``attribute`` has the given ``value``. + + ``attribute`` is either a unicode string or a byte string, containing + the name of a sys attribute to match. ``value`` is an attribute value, + being one of the following types: + + - :func:`int`, + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + If ``nomatch`` is ``True`` (default is ``False``), the match is + inverted: A device is include if the ``attribute`` does *not* match + the given ``value``. + + .. note:: + + If ``nomatch`` is ``True``, devices which do not have the given + ``attribute`` at all are also included. In other words, with + ``nomatch=True`` the given ``attribute`` is *not* guaranteed to + exist on all returned devices. + + Return the instance again. + """ + match = (self._libudev.udev_enumerate_add_match_sysattr + if not nomatch else + self._libudev.udev_enumerate_add_nomatch_sysattr) + match(self, ensure_byte_string(attribute), + property_value_to_bytes(value)) + return self + + def match_tag(self, tag): + """ + Include all devices, which have the given ``tag`` attached. + + ``tag`` is a byte or unicode string containing the tag name. + + Return the instance again. + + .. udevversion:: 154 + + .. versionadded:: 0.6 + """ + self._libudev.udev_enumerate_add_match_tag(self, ensure_byte_string(tag)) + return self + + def match_is_initialized(self): + """ + Include only devices, which are initialized. + + Initialized devices have properly set device node permissions and + context, and are (in case of network devices) fully renamed. + + Currently this will not affect devices which do not have device nodes + and are not network interfaces. + + Return the instance again. + + .. seealso:: :attr:`Device.is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_is_initialized(self) + return self + + def match_parent(self, parent): + """ + Include all devices on the subtree of the given ``parent`` device. + + The ``parent`` device itself is also included. + + ``parent`` is a :class:`~pyudev.Device`. + + Return the instance again. + + .. udevversion:: 172 + + .. versionadded:: 0.13 + """ + self._libudev.udev_enumerate_add_match_parent(self, parent) + return self + + def __iter__(self): + """ + Iterate over all matching devices. + + Yield :class:`Device` objects. + """ + self._libudev.udev_enumerate_scan_devices(self) + entry = self._libudev.udev_enumerate_get_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, entry): + try: + yield Devices.from_sys_path(self.context, name) + except DeviceNotFoundAtPathError: + continue diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/__init__.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/__init__.py new file mode 100644 index 0000000..14c9c63 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device + ============= + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from ._device import Attributes +from ._device import Device +from ._device import Devices +from ._device import Tags diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/_device.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/_device.py new file mode 100644 index 0000000..94dd41c --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/device/_device.py @@ -0,0 +1,1282 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.device._device + ===================== + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import re +from collections import Container +from collections import Iterable +from collections import Mapping +from datetime import timedelta + +from .._errors import DeviceNotFoundAtPathError +from .._errors import DeviceNotFoundByFileError +from .._errors import DeviceNotFoundByInterfaceIndexError +from .._errors import DeviceNotFoundByKernelDeviceError +from .._errors import DeviceNotFoundByNameError +from .._errors import DeviceNotFoundByNumberError +from .._errors import DeviceNotFoundInEnvironmentError +from .._util import ensure_byte_string +from .._util import ensure_unicode_string +from .._util import get_device_type +from .._util import string_to_bool +from .._util import udev_list_iterate + +# pylint: disable=too-many-lines + +class Devices(object): + """ + Class for constructing :class:`Device` objects from various kinds of data. + """ + + @classmethod + def from_path(cls, context, path): + """ + Create a device from a device ``path``. The ``path`` may or may not + start with the ``sysfs`` mount point: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_path(context, '/devices/platform') + Device(u'/sys/devices/platform') + >>> Devices.from_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``path`` is a device path as unicode or byte string. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for ``path``. + + .. versionadded:: 0.18 + """ + if not path.startswith(context.sys_path): + path = os.path.join(context.sys_path, path.lstrip(os.sep)) + return cls.from_sys_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): + """ + Create a new device from a given ``sys_path``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_sys_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``sys_path`` is a unicode or byte string containing the path of the + device inside ``sysfs`` with the mount point included. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for + ``sys_path``. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_syspath( + context, ensure_byte_string(sys_path)) + if not device: + raise DeviceNotFoundAtPathError(sys_path) + return Device(context, device) + + @classmethod + def from_name(cls, context, subsystem, sys_name): + """ + Create a new device from a given ``subsystem`` and a given + ``sys_name``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda + Device(u'/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda') + >>> sda == Devices.from_path(context, '/block/sda') + + ``context`` is the :class:`Context` in which to search the device. + ``subsystem`` and ``sys_name`` are byte or unicode strings, which + denote the subsystem and the name of the device to create. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundByNameError`, if no device was found with the given + name. + + .. versionadded:: 0.18 + """ + sys_name = sys_name.replace("/", "!") + device = context._libudev.udev_device_new_from_subsystem_sysname( + context, ensure_byte_string(subsystem), + ensure_byte_string(sys_name)) + if not device: + raise DeviceNotFoundByNameError(subsystem, sys_name) + return Device(context, device) + + @classmethod + def from_device_number(cls, context, typ, number): + """ + Create a new device from a device ``number`` with the given device + ``type``: + + >>> import os + >>> from pyudev import Context, Device + >>> ctx = Context() + >>> major, minor = 8, 0 + >>> device = Devices.from_device_number(context, 'block', + ... os.makedev(major, minor)) + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:11.0/host0/target0:0:0/0:0:0:0/block/sda') + >>> os.major(device.device_number), os.minor(device.device_number) + (8, 0) + + Use :func:`os.makedev` to construct a device number from a major and a + minor device number, as shown in the example above. + + .. warning:: + + Device numbers are not unique across different device types. + Passing a correct number with a wrong type may silently yield a + wrong device object, so make sure to pass the correct device type. + + ``context`` is the :class:`Context`, in which to search the device. + ``type`` is either ``'char'`` or ``'block'``, according to whether the + device is a character or block device. ``number`` is the device number + as integer. + + Return a :class:`Device` object for the device with the given device + ``number``. Raise :exc:`DeviceNotFoundByNumberError`, if no device was + found with the given device type and number. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_devnum( + context, ensure_byte_string(typ[0]), number) + if not device: + raise DeviceNotFoundByNumberError(typ, number) + return Device(context, device) + + @classmethod + def from_device_file(cls, context, filename): + """ + Create a new device from the given device file: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_device_file(context, '/dev/sda') + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') + >>> device.device_node + u'/dev/sda' + + .. warning:: + + Though the example seems to suggest that ``device.device_node == + filename`` holds with ``device = Devices.from_device_file(context, + filename)``, this is only true in a majority of cases. There *can* + be devices, for which this relation is actually false! Thus, do + *not* expect :attr:`~Device.device_node` to be equal to the given + ``filename`` for the returned :class:`Device`. Especially, use + :attr:`~Device.device_node` if you need the device file of a + :class:`Device` created with this method afterwards. + + ``context`` is the :class:`Context` in which to search the device. + ``filename`` is a string containing the path of a device file. + + Return a :class:`Device` representing the given device file. Raise + :exc:`DeviceNotFoundByFileError` if ``filename`` is no device file + at all or if ``filename`` does not exist or if its metadata was + inaccessible. + + .. versionadded:: 0.18 + """ + try: + device_type = get_device_type(filename) + device_number = os.stat(filename).st_rdev + except (EnvironmentError, ValueError) as err: + raise DeviceNotFoundByFileError(err) + + return cls.from_device_number(context, device_type, device_number) + + + @classmethod + def from_interface_index(cls, context, ifindex): + """ + Locate a device based on the interface index. + + :param `Context` context: the libudev context + :param int ifindex: the interface index + :returns: the device corresponding to the interface index + :rtype: `Device` + + This method is only appropriate for network devices. + """ + network_devices = context.list_devices(subsystem='net') + dev = next( + (d for d in network_devices if \ + d.attributes.get('ifindex') == ifindex), + None + ) + if dev is not None: + return dev + else: + raise DeviceNotFoundByInterfaceIndexError(ifindex) + + + @classmethod + def from_kernel_device(cls, context, kernel_device): + """ + Locate a device based on the kernel device. + + :param `Context` context: the libudev context + :param str kernel_device: the kernel device + :returns: the device corresponding to ``kernel_device`` + :rtype: `Device` + """ + switch_char = kernel_device[0] + rest = kernel_device[1:] + if switch_char in ('b', 'c'): + number_re = re.compile(r'^(?P\d+):(?P\d+)$') + match = number_re.match(rest) + if match: + number = os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + return cls.from_device_number(context, switch_char, number) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + elif switch_char == 'n': + return cls.from_interface_index(context, rest) + elif switch_char == '+': + (subsystem, _, kernel_device_name) = rest.partition(':') + if kernel_device_name and subsystem: + return cls.from_name(context, subsystem, kernel_device_name) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + + + @classmethod + def from_environment(cls, context): + """ + Create a new device from the process environment (as in + :data:`os.environ`). + + This only works reliable, if the current process is called from an + udev rule, and is usually used for tools executed from ``IMPORT=`` + rules. Use this method to create device objects in Python scripts + called from udev rules. + + ``context`` is the library :class:`Context`. + + Return a :class:`Device` object constructed from the environment. + Raise :exc:`DeviceNotFoundInEnvironmentError`, if no device could be + created from the environment. + + .. udevversion:: 152 + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_environment(context) + if not device: + raise DeviceNotFoundInEnvironmentError() + return Device(context, device) + + @classmethod + def METHODS(cls): # pylint: disable=invalid-name + """ + Return methods that obtain a :class:`Device` from a variety of + different data. + + :return: a list of from_* methods. + :rtype: list of class methods + + .. versionadded:: 0.18 + """ + return [ #pragma: no cover + cls.from_device_file, + cls.from_device_number, + cls.from_name, + cls.from_path, + cls.from_sys_path + ] + + +class Device(Mapping): + # pylint: disable=too-many-public-methods + """ + A single device with attached attributes and properties. + + A device also has a set of udev-specific attributes like the path + inside ``sysfs``. + + :class:`Device` objects compare equal and unequal to other devices and + to strings (based on :attr:`device_path`). However, there is no + ordering on :class:`Device` objects, and the corresponding operators + ``>``, ``<``, ``<=`` and ``>=`` raise :exc:`~exceptions.TypeError`. + + .. warning:: + + Currently, Device extends Mapping. The mapping that it stores is that + of udev property names to udev property values. This use is deprecated + and Device will no longer extend Mapping in 1.0. To look up udev + properties, use the Device.properties property. + + .. warning:: + + **Never** use object identity (``is`` operator) to compare + :class:`Device` objects. :mod:`pyudev` may create multiple + :class:`Device` objects for the same device. Instead compare + devices by value using ``==`` or ``!=``. + + :class:`Device` objects are hashable and can therefore be used as keys + in dictionaries and sets. + + They can also be given directly as ``udev_device *`` to functions wrapped + through :mod:`ctypes`. + """ + + @classmethod + def from_path(cls, context, path): #pragma: no cover + """ + .. versionadded:: 0.4 + .. deprecated:: 0.18 + Use :class:`Devices.from_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): #pragma: no cover + """ + .. versionchanged:: 0.4 + Raise :exc:`NoSuchDeviceError` instead of returning ``None``, if + no device was found for ``sys_path``. + .. versionchanged:: 0.5 + Raise :exc:`DeviceNotFoundAtPathError` instead of + :exc:`NoSuchDeviceError`. + .. deprecated:: 0.18 + Use :class:`Devices.from_sys_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_sys_path(context, sys_path) + + @classmethod + def from_name(cls, context, subsystem, sys_name): #pragma: no cover + """ + .. versionadded:: 0.5 + .. deprecated:: 0.18 + Use :class:`Devices.from_name` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_name(context, subsystem, sys_name) + + @classmethod + def from_device_number(cls, context, typ, number): #pragma: no cover + """ + .. versionadded:: 0.11 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_number` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_number(context, typ, number) + + @classmethod + def from_device_file(cls, context, filename): #pragma: no cover + """ + .. versionadded:: 0.15 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_file` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_file(context, filename) + + @classmethod + def from_environment(cls, context): #pragma: no cover + """ + .. versionadded:: 0.6 + .. deprecated:: 0.18 + Use :class:`Devices.from_environment` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_environment(context) + + def __init__(self, context, _device): + self.context = context + self._as_parameter_ = _device + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_device_unref(self) + + def __repr__(self): + return 'Device({0.sys_path!r})'.format(self) + + @property + def parent(self): + """ + The parent :class:`Device` or ``None``, if there is no parent + device. + """ + parent = self._libudev.udev_device_get_parent(self) + if not parent: + return None + # the parent device is not referenced, thus forcibly acquire a + # reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + @property + def children(self): + """ + Yield all direct children of this device. + + .. note:: + + In udev, parent-child relationships are generally ambiguous, i.e. + a parent can have multiple children, *and* a child can have multiple + parents. Hence, `child.parent == parent` does generally *not* hold + for all `child` objects in `parent.children`. In other words, + the :attr:`parent` of a device in this property can be different + from this device! + + .. note:: + + As the underlying library does not provide any means to directly + query the children of a device, this property performs a linear + search through all devices. + + Return an iterable yielding a :class:`Device` object for each direct + child of this device. + + .. udevversion:: 172 + + .. versionchanged:: 0.13 + Requires udev version 172 now. + """ + for device in self.context.list_devices().match_parent(self): + if device != self: + yield device + + @property + def ancestors(self): + """ + Yield all ancestors of this device from bottom to top. + + Return an iterator yielding a :class:`Device` object for each + ancestor of this device from bottom to top. + + .. versionadded:: 0.16 + """ + parent = self.parent + while parent is not None: + yield parent + parent = parent.parent + + def find_parent(self, subsystem, device_type=None): + """ + Find the parent device with the given ``subsystem`` and + ``device_type``. + + ``subsystem`` is a byte or unicode string containing the name of the + subsystem, in which to search for the parent. ``device_type`` is a + byte or unicode string holding the expected device type of the parent. + It can be ``None`` (the default), which means, that no specific device + type is expected. + + Return a parent :class:`Device` within the given ``subsystem`` and, if + ``device_type`` is not ``None``, with the given ``device_type``, or + ``None``, if this device has no parent device matching these + constraints. + + .. versionadded:: 0.9 + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + parent = self._libudev.udev_device_get_parent_with_subsystem_devtype( + self, subsystem, device_type) + if not parent: + return None + # parent device is not referenced, thus forcibly acquire a reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + def traverse(self): + """ + Traverse all parent devices of this device from bottom to top. + + Return an iterable yielding all parent devices as :class:`Device` + objects, *not* including the current device. The last yielded + :class:`Device` is the top of the device hierarchy. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :attr:`ancestors` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.ancestors instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.ancestors + + @property + def sys_path(self): + """ + Absolute path of this device in ``sysfs`` including the ``sysfs`` + mount point as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_syspath(self)) + + @property + def device_path(self): + """ + Kernel device path as unicode string. This path uniquely identifies + a single device. + + Unlike :attr:`sys_path`, this path does not contain the ``sysfs`` + mount point. However, the path is absolute and starts with a slash + ``'/'``. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_devpath(self)) + + @property + def subsystem(self): + """ + Name of the subsystem this device is part of as unicode string. + + :returns: name of subsystem if found, else None + :rtype: unicode string or NoneType + """ + subsys = self._libudev.udev_device_get_subsystem(self) + return None if subsys is None else ensure_unicode_string(subsys) + + @property + def sys_name(self): + """ + Device file name inside ``sysfs`` as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_sysname(self)) + + @property + def sys_number(self): + """ + The trailing number of the :attr:`sys_name` as unicode string, or + ``None``, if the device has no trailing number in its name. + + .. note:: + + The number is returned as unicode string to preserve the exact + format of the number, especially any leading zeros: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_path(context, '/sys/devices/LNXSYSTM:00') + >>> device.sys_number + u'00' + + To work with numbers, explicitly convert them to ints: + + >>> int(device.sys_number) + 0 + + .. versionadded:: 0.11 + """ + number = self._libudev.udev_device_get_sysnum(self) + return ensure_unicode_string(number) if number is not None else None + + @property + def device_type(self): + """ + Device type as unicode string, or ``None``, if the device type is + unknown. + + >>> from pyudev import Context + >>> context = Context() + >>> for device in context.list_devices(subsystem='net'): + ... '{0} - {1}'.format(device.sys_name, device.device_type or 'ethernet') + ... + u'eth0 - ethernet' + u'wlan0 - wlan' + u'lo - ethernet' + u'vboxnet0 - ethernet' + + .. versionadded:: 0.10 + """ + device_type = self._libudev.udev_device_get_devtype(self) + if device_type is not None: + return ensure_unicode_string(device_type) + else: + return device_type + + @property + def driver(self): + """ + The driver name as unicode string, or ``None``, if there is no + driver for this device. + + .. versionadded:: 0.5 + """ + driver = self._libudev.udev_device_get_driver(self) + return ensure_unicode_string(driver) if driver is not None else None + + @property + def device_node(self): + """ + Absolute path to the device node of this device as unicode string or + ``None``, if this device doesn't have a device node. The path + includes the device directory (see :attr:`Context.device_path`). + + This path always points to the actual device node associated with + this device, and never to any symbolic links to this device node. + See :attr:`device_links` to get a list of symbolic links to this + device node. + + .. warning:: + + For devices created with :meth:`from_device_file()`, the value of + this property is not necessary equal to the ``filename`` given to + :meth:`from_device_file()`. + """ + node = self._libudev.udev_device_get_devnode(self) + return ensure_unicode_string(node) if node is not None else None + + @property + def device_number(self): + """ + The device number of the associated device as integer, or ``0``, if no + device number is associated. + + Use :func:`os.major` and :func:`os.minor` to decompose the device + number into its major and minor number: + + >>> import os + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda.device_number + 2048L + >>> (os.major(sda.device_number), os.minor(sda.device_number)) + (8, 0) + + For devices with an associated :attr:`device_node`, this is the same as + the ``st_rdev`` field of the stat result of the :attr:`device_node`: + + >>> os.stat(sda.device_node).st_rdev + 2048 + + .. versionadded:: 0.11 + """ + return self._libudev.udev_device_get_devnum(self) + + @property + def is_initialized(self): + """ + ``True``, if the device is initialized, ``False`` otherwise. + + A device is initialized, if udev has already handled this device and + has set up device node permissions and context, or renamed a network + device. + + Consequently, this property is only implemented for devices with a + device node or for network devices. On all other devices this property + is always ``True``. + + It is *not* recommended, that you use uninitialized devices. + + .. seealso:: :attr:`time_since_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + return bool(self._libudev.udev_device_get_is_initialized(self)) + + @property + def time_since_initialized(self): + """ + The time elapsed since initialization as :class:`~datetime.timedelta`. + + This property is only implemented on devices, which need to store + properties in the udev database. On all other devices this property is + simply zero :class:`~datetime.timedelta`. + + .. seealso:: :attr:`is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + microseconds = self._libudev.udev_device_get_usec_since_initialized( + self) + return timedelta(microseconds=microseconds) + + @property + def device_links(self): + """ + An iterator, which yields the absolute paths (including the device + directory, see :attr:`Context.device_path`) of all symbolic links + pointing to the :attr:`device_node` of this device. The paths are + unicode strings. + + UDev can create symlinks to the original device node (see + :attr:`device_node`) inside the device directory. This is often + used to assign a constant, fixed device node to devices like + removeable media, which technically do not have a constant device + node, or to map a single device into multiple device hierarchies. + The property provides access to all such symbolic links, which were + created by UDev for this device. + + .. warning:: + + Links are not necessarily resolved by + :meth:`Devices.from_device_file()`. Hence do *not* rely on + ``Devices.from_device_file(context, link).device_path == + device.device_path`` from any ``link`` in ``device.device_links``. + """ + devlinks = self._libudev.udev_device_get_devlinks_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, devlinks): + yield ensure_unicode_string(name) + + @property + def action(self): + """ + The device event action as string, or ``None``, if this device was not + received from a :class:`Monitor`. + + Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + .. warning:: + + Though the actions listed above are the most common, this property + *may* return other values, too, so be prepared to handle unknown + actions! + + .. versionadded:: 0.16 + """ + action = self._libudev.udev_device_get_action(self) + return ensure_unicode_string(action) if action is not None else None + + @property + def sequence_number(self): + """ + The device event sequence number as integer, or ``0`` if this device + has no sequence number, i.e. was not received from a :class:`Monitor`. + + .. versionadded:: 0.16 + """ + return self._libudev.udev_device_get_seqnum(self) + + @property + def attributes(self): + """ + The system attributes of this device as read-only + :class:`Attributes` mapping. + + System attributes are basically normal files inside the device + directory. These files contain all sorts of information about the + device, which may not be reflected by properties. These attributes + are commonly used for matching in udev rules, and can be printed + using ``udevadm info --attribute-walk``. + + The values of these attributes are not always proper strings, and + can contain arbitrary bytes. + + :returns: an Attributes object, useful for reading attributes + :rtype: Attributes + + .. versionadded:: 0.5 + """ + # do *not* cache the created object in an attribute of this class. + # Doing so creates an uncollectable reference cycle between Device and + # Attributes, because Attributes refers to this object through + # Attributes.device. + return Attributes(self) + + @property + def properties(self): + """ + The udev properties of this object as read-only Properties mapping. + + .. versionadded:: 0.21 + """ + return Properties(self) + + @property + def tags(self): + """ + A :class:`Tags` object representing the tags attached to this device. + + The :class:`Tags` object supports a test for a single tag as well as + iteration over all tags: + + >>> from pyudev import Context + >>> context = Context() + >>> device = next(iter(context.list_devices(tag='systemd'))) + >>> 'systemd' in device.tags + True + >>> list(device.tags) + [u'seat', u'systemd', u'uaccess'] + + Tags are arbitrary classifiers that can be attached to devices by udev + scripts and daemons. For instance, systemd_ uses tags for multi-seat_ + support. + + .. _systemd: http://freedesktop.org/wiki/Software/systemd + .. _multi-seat: http://www.freedesktop.org/wiki/Software/systemd/multiseat + + .. udevversion:: 154 + + .. versionadded:: 0.6 + + .. versionchanged:: 0.13 + Return a :class:`Tags` object now. + """ + return Tags(self) + + def __iter__(self): + """ + Iterate over the names of all properties defined for this device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__iter__() + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__len__() + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__getitem__(prop) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asint() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asint instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asint(prop) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asbool() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asbool instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asbool(prop) + + def __hash__(self): + return hash(self.device_path) + + def __eq__(self, other): + if isinstance(other, Device): + return self.device_path == other.device_path + else: + return self.device_path == other + + def __ne__(self, other): + if isinstance(other, Device): + return self.device_path != other.device_path + else: + return self.device_path != other + + def __gt__(self, other): + raise TypeError('Device not orderable') + + def __lt__(self, other): + raise TypeError('Device not orderable') + + def __le__(self, other): + raise TypeError('Device not orderable') + + def __ge__(self, other): + raise TypeError('Device not orderable') + + +class Properties(Mapping): + """ + udev properties :class:`Device` objects. + + .. versionadded:: 0.21 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def __iter__(self): + """ + Iterate over the names of all properties defined for the device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + for name, _ in udev_list_iterate(self._libudev, properties): + yield ensure_unicode_string(name) + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + return sum(1 for _ in udev_list_iterate(self._libudev, properties)) + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + """ + value = self._libudev.udev_device_get_property_value( + self.device, + ensure_byte_string(prop) + ) + if value is None: + raise KeyError(prop) + return ensure_unicode_string(value) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + """ + return int(self[prop]) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + """ + return string_to_bool(self[prop]) + + +class Attributes(object): + """ + udev attributes for :class:`Device` objects. + + .. versionadded:: 0.5 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + @property + def available_attributes(self): + """ + Yield the ``available`` attributes for the device. + + It is not guaranteed that a key in this list will have a value. + It is not guaranteed that a key not in this list will not have a value. + + It is guaranteed that the keys in this list are the keys that libudev + considers to be "available" attributes. + + If libudev version does not define udev_device_get_sysattr_list_entry() + yields nothing. + + See rhbz#1267584. + """ + if not hasattr(self._libudev, 'udev_device_get_sysattr_list_entry'): + return # pragma: no cover + attrs = self._libudev.udev_device_get_sysattr_list_entry(self.device) + for attribute, _ in udev_list_iterate(self._libudev, attrs): + yield ensure_unicode_string(attribute) + + def _get(self, attribute): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute`` + :rtype: an arbitrary sequence of bytes + :raises KeyError: if no value found + """ + value = self._libudev.udev_device_get_sysattr_value( + self.device, + ensure_byte_string(attribute) + ) + if value is None: + raise KeyError(attribute) + return value + + def get(self, attribute, default=None): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :param default: a default if no corresponding value found + :type default: a sequence of bytes + :returns: the value corresponding to ``attribute`` or ``default`` + :rtype: object + """ + try: + return self._get(attribute) + except KeyError: + return default + + def asstring(self, attribute): + """ + Get the given ``attribute`` for the device as unicode string. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as unicode + :rtype: unicode + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible + """ + return ensure_unicode_string(self._get(attribute)) + + def asint(self, attribute): + """ + Get the given ``attribute`` as an int. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as an int + :rtype: int + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to an int + """ + return int(self.asstring(attribute)) + + def asbool(self, attribute): + """ + Get the given ``attribute`` from this device as a bool. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as bool + :rtype: bool + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to a bool + + A boolean attribute has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + """ + return string_to_bool(self.asstring(attribute)) + + +class Tags(Iterable, Container): + """ + A iterable over :class:`Device` tags. + + Subclasses the ``Container`` and the ``Iterable`` ABC. + """ + + # pylint: disable=too-few-public-methods + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def _has_tag(self, tag): + """ + Whether ``tag`` exists. + + :param tag: unicode string with name of tag + :rtype: bool + """ + if hasattr(self._libudev, 'udev_device_has_tag'): + return bool(self._libudev.udev_device_has_tag( + self.device, ensure_byte_string(tag))) + else: # pragma: no cover + return any(t == tag for t in self) + + def __contains__(self, tag): + """ + Check for existence of ``tag``. + + ``tag`` is a tag as unicode string. + + Return ``True``, if ``tag`` is attached to the device, ``False`` + otherwise. + """ + return self._has_tag(tag) + + def __iter__(self): + """ + Iterate over all tags. + + Yield each tag as unicode string. + """ + tags = self._libudev.udev_device_get_tags_list_entry(self.device) + for tag, _ in udev_list_iterate(self._libudev, tags): + yield ensure_unicode_string(tag) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/discover.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/discover.py new file mode 100644 index 0000000..f2b4261 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/discover.py @@ -0,0 +1,391 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.discover + =============== + + Tools to discover a device given limited information. + + .. moduleauthor:: mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc +import functools +import os +import re +import six + +from ._errors import DeviceNotFoundError + +from .device import Devices + + +def wrap_exception(func): + """ + Allow Device discovery methods to return None instead of raising an + exception. + """ + + @functools.wraps(func) + def the_func(*args, **kwargs): + """ + Returns result of calling ``func`` on ``args``, ``kwargs``. + Returns None if ``func`` raises :exc:`DeviceNotFoundError`. + """ + try: + return func(*args, **kwargs) + except DeviceNotFoundError: + return None + + return the_func + +@six.add_metaclass(abc.ABCMeta) +class Hypothesis(object): + """ + Represents a hypothesis about the meaning of the device identifier. + """ + + @classmethod + @abc.abstractmethod + def match(cls, value): # pragma: no cover + """ + Match the given string according to the hypothesis. + + The purpose of this method is to obtain a value corresponding to + ``value`` if that is possible. It may use a regular expression, but + in general it should just return ``value`` and let the lookup method + sort out the rest. + + :param str value: the string to inspect + :returns: the matched thing or None if unmatched + :rtype: the type of lookup's key parameter or NoneType + """ + raise NotImplementedError() + + @classmethod + @abc.abstractmethod + def lookup(cls, context, key): # pragma: no cover + """ + Lookup the given string according to the hypothesis. + + :param Context context: the pyudev context + :param key: a key with which to lookup the device + :type key: the type of match's return value if not None + :returns: a list of Devices obtained + :rtype: frozenset of :class:`Device` + """ + raise NotImplementedError() + + @classmethod + def setup(cls, context): + """ + A potentially expensive method that may allow an :class:`Hypothesis` + to find devices more rapidly or to find a device that it would + otherwise miss. + + :param Context context: the pyudev context + """ + pass + + @classmethod + def get_devices(cls, context, value): + """ + Get any devices that may correspond to the given string. + + :param Context context: the pyudev context + :param str value: the value to look for + :returns: a list of devices obtained + :rtype: set of :class:`Device` + """ + key = cls.match(value) + return cls.lookup(context, key) if key is not None else frozenset() + + +class DeviceNumberHypothesis(Hypothesis): + """ + Represents the hypothesis that the device is a device number. + + The device may be separated into major/minor number or a composite number. + """ + + @classmethod + def _match_major_minor(cls, value): + """ + Match the number under the assumption that it is a major,minor pair. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + major_minor_re = re.compile( + r'^(?P\d+)(\D+)(?P\d+)$' + ) + match = major_minor_re.match(value) + return match and os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + + @classmethod + def _match_number(cls, value): + """ + Match the number under the assumption that it is a single number. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + number_re = re.compile(r'^(?P\d+)$') + match = number_re.match(value) + return match and int(match.group('number')) + + @classmethod + def match(cls, value): + """ + Match the number under the assumption that it is a device number. + + :returns: the device number or None + :rtype: int or NoneType + """ + return cls._match_major_minor(value) or cls._match_number(value) + + @classmethod + def find_subsystems(cls, context): + """ + Find subsystems in /sys/dev. + + :param Context context: the context + :returns: a lis of available subsystems + :rtype: list of str + """ + sys_path = context.sys_path + return os.listdir(os.path.join(sys_path, 'dev')) + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the device number. + + :param Context context: the context + :param int key: the device number + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_device_number) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DevicePathHypothesis(Hypothesis): + """ + Discover the device assuming the identifier is a device path. + """ + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device path. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + res = wrap_exception(Devices.from_path)(context, key) + return frozenset((res,)) if res is not None else frozenset() + + +class DeviceNameHypothesis(Hypothesis): + """ + Discover the device assuming the input is a device name. + + Try every available subsystem. + """ + + @classmethod + def find_subsystems(cls, context): + """ + Find all subsystems in sysfs. + + :param Context context: the context + :rtype: frozenset + :returns: subsystems in sysfs + """ + sys_path = context.sys_path + dirnames = ('bus', 'class', 'subsystem') + absnames = (os.path.join(sys_path, name) for name in dirnames) + realnames = (d for d in absnames if os.path.isdir(d)) + return frozenset(n for d in realnames for n in os.listdir(d)) + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device name. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_name) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DeviceFileHypothesis(Hypothesis): + """ + Discover the device assuming the value is some portion of a device file. + + The device file may be a link to a device node. + """ + + _LINK_DIRS = [ + '/dev', + '/dev/disk/by-id', + '/dev/disk/by-label', + '/dev/disk/by-partlabel', + '/dev/disk/by-partuuid', + '/dev/disk/by-path', + '/dev/disk/by-uuid', + '/dev/input/by-path', + '/dev/mapper', + '/dev/md', + '/dev/vg' + ] + + @classmethod + def get_link_dirs(cls, context): + """ + Get all directories that may contain links to device nodes. + + This method checks the device links of every device, so it is very + expensive. + + :param Context context: the context + :returns: a sorted list of directories that contain device links + :rtype: list + """ + devices = context.list_devices() + devices_with_links = (d for d in devices if list(d.device_links)) + links = (l for d in devices_with_links for l in d.device_links) + return sorted(set(os.path.dirname(l) for l in links)) + + @classmethod + def setup(cls, context): + """ + Set the link directories to be used when discovering by file. + + Uses `get_link_dirs`, so is as expensive as it is. + + :param Context context: the context + """ + cls._LINK_DIRS = cls.get_link_dirs(context) + + @classmethod + def match(cls, value): + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup the device under the assumption that the key is part of + the name of a device file. + + :param Context context: the context + :param str key: a portion of the device file name + + It is assumed that either it is the whole name of the device file + or it is the basename. + + A device file may be a device node or a device link. + """ + func = wrap_exception(Devices.from_device_file) + if '/' in key: + device = func(context, key) + return frozenset((device,)) if device is not None else frozenset() + else: + files = (os.path.join(ld, key) for ld in cls._LINK_DIRS) + devices = (func(context, f) for f in files) + return frozenset(d for d in devices if d is not None) + + +class Discovery(object): + # pylint: disable=too-few-public-methods + """ + Provides discovery methods for devices. + """ + + _HYPOTHESES = [ + DeviceFileHypothesis, + DeviceNameHypothesis, + DeviceNumberHypothesis, + DevicePathHypothesis + ] + + def __init__(self): + self._hypotheses = self._HYPOTHESES + + def setup(self, context): + """ + Set up individual hypotheses. + + May be an expensive call. + + :param Context context: the context + """ + for hyp in self._hypotheses: + hyp.setup(context) + + def get_devices(self, context, value): + """ + Get the devices corresponding to value. + + :param Context context: the context + :param str value: some identifier of the device + :returns: a list of corresponding devices + :rtype: frozenset of :class:`Device` + """ + return frozenset( + d for h in self._hypotheses for d in h.get_devices(context, value) + ) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/glib.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/glib.py new file mode 100644 index 0000000..4cb5c0e --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/glib.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""pyudev.glib + =========== + + Glib integration. + + :class:`MonitorObserver` integrates device monitoring into the Glib + mainloop by turing device events into Glib signals. + + :mod:`glib` and :mod:`gobject` from PyGObject_ must be available when + importing this module. PyGtk is not required. + + .. _PyGObject: http://www.pygtk.org/ + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.7 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +# thanks to absolute imports, this really imports the glib binding and not this +# module again +import glib +import gobject + + +class _ObserverMixin(object): + """Mixin to provide observer behavior to the old and the new API.""" + # pylint: disable=too-few-public-methods + + def _setup_observer(self, monitor): + # pylint: disable=attribute-defined-outside-init + self.monitor = monitor + self.event_source = None + self.enabled = True + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + + .. versionadded:: 0.14 + """ + return self.event_source is not None + + @enabled.setter + def enabled(self, value): + if value and self.event_source is None: + # pylint: disable=attribute-defined-outside-init + self.event_source = glib.io_add_watch( + self.monitor, glib.IO_IN, self._process_udev_event) + elif not value and self.event_source is not None: + glib.source_remove(self.event_source) + + def _process_udev_event(self, source, condition): + # pylint: disable=unused-argument + if condition == glib.IO_IN: + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + return True + + def _emit_event(self, device): + self.emit('device-event', device) + + +class MonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + This class inherits :class:`~gobject.GObject` to turn device events into + glib signals. + + >>> from pyudev import Context, Monitor + >>> from pyudev.glib import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(observer, device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.connect('device-event', device_event) + >>> monitor.start() + + This class is a child of :class:`gobject.GObject`. + """ + + __gsignals__ = { + # explicitly convert the signal to str, because glib expects the + # *native* string type of the corresponding python version as type of + # signal name, and str() is the name of the native string type of both + # python versions. We could also remove the "unicode_literals" import, + # but I don't want to make exceptions to the standard set of future + # imports used throughout pyudev for the sake of consistency. + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + + +gobject.type_register(MonitorObserver) + + +class GUDevMonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_signal_map = { + 'add': 'device-added', 'remove': 'device-removed', + 'change': 'device-changed', 'move': 'device-moved'} + + __gsignals__ = { + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)), + str('device-added'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-removed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-changed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-moved'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.glib.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.emit('device-event', device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + self.emit(signal, device) + + +gobject.type_register(GUDevMonitorObserver) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/monitor.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/monitor.py new file mode 100644 index 0000000..ec3e7e4 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/monitor.py @@ -0,0 +1,582 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.monitor + ============== + + Monitor implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +import os +import errno +from threading import Thread +from functools import partial + +from .device import Device + +from ._util import eintr_retry_call +from ._util import ensure_byte_string + +from ._os import pipe +from ._os import poll + + +class Monitor(object): + """ + A synchronous device event monitor. + + A :class:`Monitor` objects connects to the udev daemon and listens for + changes to the device list. A monitor is created by connecting to the + kernel daemon through netlink (see :meth:`from_netlink`): + + >>> from pyudev import Context, Monitor + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + + Once the monitor is created, you can add a filter using :meth:`filter_by()` + or :meth:`filter_by_tag()` to drop incoming events in subsystems, which are + not of interest to the application: + + >>> monitor.filter_by('input') + + When the monitor is eventually set up, you can either poll for events + synchronously: + + >>> device = monitor.poll(timeout=3) + >>> if device: + ... print('{0.action}: {0}'.format(device)) + ... + + Or you can monitor events asynchronously with :class:`MonitorObserver`. + + To integrate into various event processing frameworks, the monitor provides + a :func:`selectable ` file description by :meth:`fileno()`. + However, do *not* read or write directly on this file descriptor. + + Instances of this class can directly be given as ``udev_monitor *`` to + functions wrapped through :mod:`ctypes`. + + .. versionchanged:: 0.16 + Remove :meth:`from_socket()` which is deprecated, and even removed in + recent udev versions. + """ + + def __init__(self, context, monitor_p): + self.context = context + self._as_parameter_ = monitor_p + self._libudev = context._libudev + self._started = False + + def __del__(self): + self._libudev.udev_monitor_unref(self) + + @classmethod + def from_netlink(cls, context, source='udev'): + """ + Create a monitor by connecting to the kernel daemon through netlink. + + ``context`` is the :class:`Context` to use. ``source`` is a string, + describing the event source. Two sources are available: + + ``'udev'`` (the default) + Events emitted after udev as registered and configured the device. + This is the absolutely recommended source for applications. + + ``'kernel'`` + Events emitted directly after the kernel has seen the device. The + device has not yet been configured by udev and might not be usable + at all. **Never** use this, unless you know what you are doing. + + Return a new :class:`Monitor` object, which is connected to the + given source. Raise :exc:`~exceptions.ValueError`, if an invalid + source has been specified. Raise + :exc:`~exceptions.EnvironmentError`, if the creation of the monitor + failed. + """ + if source not in ('kernel', 'udev'): + raise ValueError('Invalid source: {0!r}. Must be one of "udev" ' + 'or "kernel"'.format(source)) + monitor = context._libudev.udev_monitor_new_from_netlink( + context, ensure_byte_string(source)) + if not monitor: + raise EnvironmentError('Could not create udev monitor') + return cls(context, monitor) + + @property + def started(self): + """ + ``True``, if this monitor was started, ``False`` otherwise. Readonly. + + .. seealso:: :meth:`start()` + .. versionadded:: 0.16 + """ + return self._started + + def fileno(self): + # pylint: disable=anomalous-backslash-in-string + """ + Return the file description associated with this monitor as integer. + + This is really a real file descriptor ;), which can be watched and + :func:`select.select`\ ed. + """ + return self._libudev.udev_monitor_get_fd(self) + + def filter_by(self, subsystem, device_type=None): + """ + Filter incoming events. + + ``subsystem`` is a byte or unicode string with the name of a + subsystem (e.g. ``'input'``). Only events originating from the + given subsystem pass the filter and are handed to the caller. + + If given, ``device_type`` is a byte or unicode string specifying the + device type. Only devices with the given device type are propagated + to the caller. If ``device_type`` is not given, no additional + filter for a specific device type is installed. + + These filters are executed inside the kernel, and client processes + will usually not be woken up for device, that do not match these + filters. + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + self._libudev.udev_monitor_filter_add_match_subsystem_devtype( + self, subsystem, device_type) + self._libudev.udev_monitor_filter_update(self) + + def filter_by_tag(self, tag): + """ + Filter incoming events by the given ``tag``. + + ``tag`` is a byte or unicode string with the name of a tag. Only + events for devices which have this tag attached pass the filter and are + handed to the caller. + + Like with :meth:`filter_by` this filter is also executed inside the + kernel, so that client processes are usually not woken up for devices + without the given ``tag``. + + .. udevversion:: 154 + + .. versionadded:: 0.9 + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + self._libudev.udev_monitor_filter_add_match_tag( + self, ensure_byte_string(tag)) + self._libudev.udev_monitor_filter_update(self) + + def remove_filter(self): + """ + Remove any filters installed with :meth:`filter_by()` or + :meth:`filter_by_tag()` from this monitor. + + .. warning:: + + Up to udev 181 (and possibly even later versions) the underlying + ``udev_monitor_filter_remove()`` seems to be broken. If used with + affected versions this method always raises + :exc:`~exceptions.ValueError`. + + Raise :exc:`~exceptions.EnvironmentError` if removal of installed + filters failed. + + .. versionadded:: 0.15 + """ + self._libudev.udev_monitor_filter_remove(self) + self._libudev.udev_monitor_filter_update(self) + + def enable_receiving(self): + """ + Switch the monitor into listing mode. + + Connect to the event source and receive incoming events. Only after + calling this method, the monitor listens for incoming events. + + .. note:: + + This method is implicitly called by :meth:`__iter__`. You don't + need to call it explicitly, if you are iterating over the + monitor. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`start()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.start() instead.', + DeprecationWarning) + self.start() + + def start(self): + """ + Start this monitor. + + The monitor will not receive events until this method is called. This + method does nothing if called on an already started :class:`Monitor`. + + .. note:: + + Typically you don't need to call this method. It is implicitly + called by :meth:`poll()` and :meth:`__iter__()`. + + .. seealso:: :attr:`started` + .. versionchanged:: 0.16 + This method does nothing if the :class:`Monitor` was already + started. + """ + if not self._started: + self._libudev.udev_monitor_enable_receiving(self) + # Force monitor FD into non-blocking mode + pipe.set_fd_status_flag(self, os.O_NONBLOCK) + self._started = True + + def set_receive_buffer_size(self, size): + """ + Set the receive buffer ``size``. + + ``size`` is the requested buffer size in bytes, as integer. + + .. note:: + + The CAP_NET_ADMIN capability must be contained in the effective + capability set of the caller for this method to succeed. Otherwise + :exc:`~exceptions.EnvironmentError` will be raised, with ``errno`` + set to :data:`~errno.EPERM`. Unprivileged processes typically lack + this capability. You can check the capabilities of the current + process with the python-prctl_ module: + + >>> import prctl + >>> prctl.cap_effective.net_admin + + Raise :exc:`~exceptions.EnvironmentError`, if the buffer size could not + bet set. + + .. versionadded:: 0.13 + + .. _python-prctl: http://packages.python.org/python-prctl + """ + self._libudev.udev_monitor_set_receive_buffer_size(self, size) + + def _receive_device(self): + """Receive a single device from the monitor. + + Return the received :class:`Device`, or ``None`` if no device could be + received. + + """ + while True: + try: + device_p = self._libudev.udev_monitor_receive_device(self) + return Device(self.context, device_p) if device_p else None + except EnvironmentError as error: + if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): + # No data available + return None + elif error.errno == errno.EINTR: + # Try again if our system call was interrupted + continue + else: + raise + + def poll(self, timeout=None): + """ + Poll for a device event. + + You can use this method together with :func:`iter()` to synchronously + monitor events in the current thread:: + + for device in iter(monitor.poll, None): + print('{0.action} on {0.device_path}'.format(device)) + + Since this method will never return ``None`` if no ``timeout`` is + specified, this is effectively an endless loop. With + :func:`functools.partial()` you can also create a loop that only waits + for a specified time:: + + for device in iter(partial(monitor.poll, 3), None): + print('{0.action} on {0.device_path}'.format(device)) + + This loop will only wait three seconds for a new device event. If no + device event occurred after three seconds, the loop will exit. + + ``timeout`` is a floating point number that specifies a time-out in + seconds. If omitted or ``None``, this method blocks until a device + event is available. If ``0``, this method just polls and will never + block. + + .. note:: + + This method implicitly calls :meth:`start()`. + + Return the received :class:`Device`, or ``None`` if a timeout + occurred. Raise :exc:`~exceptions.EnvironmentError` if event retrieval + failed. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.16 + """ + if timeout is not None and timeout > 0: + # .poll() takes timeout in milliseconds + timeout = int(timeout * 1000) + self.start() + if eintr_retry_call(poll.Poll.for_events((self, 'r')).poll, timeout): + return self._receive_device() + else: + return None + + def receive_device(self): + """ + Receive a single device from the monitor. + + .. warning:: + + You *must* call :meth:`start()` before calling this method. + + The caller must make sure, that there are events available in the + event queue. The call blocks, until a device is available. + + If a device was available, return ``(action, device)``. ``device`` + is the :class:`Device` object describing the device. ``action`` is + a string describing the action. Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + Raise :exc:`~exceptions.EnvironmentError`, if no device could be + read. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`Monitor.poll()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.poll() instead.', + DeprecationWarning) + device = self.poll() + return device.action, device + + def __iter__(self): + """ + Wait for incoming events and receive them upon arrival. + + This methods implicitly calls :meth:`start()`, and starts polling the + :meth:`fileno` of this monitor. If a event comes in, it receives the + corresponding device and yields it to the caller. + + The returned iterator is endless, and continues receiving devices + without ever stopping. + + Yields ``(action, device)`` (see :meth:`receive_device` for a + description). + + .. deprecated:: 0.16 + Will be removed in 1.0. Use an explicit loop over :meth:`poll()` + instead, or monitor asynchronously with :class:`MonitorObserver`. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use an explicit loop over ' + '"poll()" instead, or monitor asynchronously with ' + '"MonitorObserver".', DeprecationWarning) + self.start() + while True: + device = self.poll() + if device is not None: + yield device.action, device + + +class MonitorObserver(Thread): + """ + An asynchronous observer for device events. + + This class subclasses :class:`~threading.Thread` class to asynchronously + observe a :class:`Monitor` in a background thread: + + >>> from pyudev import Context, Monitor, MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> def print_device_event(device): + ... print('background event {0.action}: {0.device_path}'.format(device)) + >>> observer = MonitorObserver(monitor, callback=print_device_event, name='monitor-observer') + >>> observer.daemon + True + >>> observer.start() + + In the above example, input device events will be printed in background, + until :meth:`stop()` is called on ``observer``. + + .. note:: + + Instances of this class are always created as daemon thread. If you do + not want to use daemon threads for monitoring, you need explicitly set + :attr:`~threading.Thread.daemon` to ``False`` before invoking + :meth:`~threading.Thread.start()`. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.14 + + .. versionchanged:: 0.15 + :meth:`Monitor.start()` is implicitly called when the thread is started. + """ + + def __init__(self, monitor, event_handler=None, callback=None, *args, + **kwargs): + """ + Create a new observer for the given ``monitor``. + + ``monitor`` is the :class:`Monitor` to observe. ``callback`` is the + callable to invoke on events, with the signature ``callback(device)`` + where ``device`` is the :class:`Device` that caused the event. + + .. warning:: + + ``callback`` is invoked in the observer thread, hence the observer + is blocked while callback executes. + + ``args`` and ``kwargs`` are passed unchanged to the constructor of + :class:`~threading.Thread`. + + .. deprecated:: 0.16 + The ``event_handler`` argument will be removed in 1.0. Use + the ``callback`` argument instead. + .. versionchanged:: 0.16 + Add ``callback`` argument. + """ + if callback is None and event_handler is None: + raise ValueError('callback missing') + elif callback is not None and event_handler is not None: + raise ValueError('Use either callback or event handler') + + Thread.__init__(self, *args, **kwargs) + self.monitor = monitor + # observer threads should not keep the interpreter alive + self.daemon = True + self._stop_event = None + if event_handler is not None: + import warnings + warnings.warn('"event_handler" argument will be removed in 1.0. ' + 'Use Monitor.poll() instead.', DeprecationWarning) + callback = lambda d: event_handler(d.action, d) + self._callback = callback + + def start(self): + """Start the observer thread.""" + if not self.is_alive(): + self._stop_event = pipe.Pipe.open() + Thread.start(self) + + def run(self): + self.monitor.start() + notifier = poll.Poll.for_events( + (self.monitor, 'r'), (self._stop_event.source, 'r')) + while True: + for file_descriptor, event in eintr_retry_call(notifier.poll): + if file_descriptor == self._stop_event.source.fileno(): + # in case of a stop event, close our pipe side, and + # return from the thread + self._stop_event.source.close() + return + elif file_descriptor == self.monitor.fileno() and event == 'r': + read_device = partial(eintr_retry_call, self.monitor.poll, timeout=0) + for device in iter(read_device, None): + self._callback(device) + else: + raise EnvironmentError('Observed monitor hung up') + + def send_stop(self): + """ + Send a stop signal to the background thread. + + The background thread will eventually exit, but it may still be running + when this method returns. This method is essentially the asynchronous + equivalent to :meth:`stop()`. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + """ + if self._stop_event is None: + return + with self._stop_event.sink: + # emit a stop event to the thread + eintr_retry_call(self._stop_event.sink.write, b'\x01') + self._stop_event.sink.flush() + + def stop(self): + """ + Synchronously stop the background thread. + + .. note:: + + This method can safely be called from the observer thread. In this + case it is equivalent to :meth:`send_stop()`. + + Send a stop signal to the backgroud (see :meth:`send_stop`), and waits + for the background thread to exit (see :meth:`~threading.Thread.join`) + if the current thread is *not* the observer thread. + + After this method returns in a thread *that is not the observer + thread*, the ``callback`` is guaranteed to not be invoked again + anymore. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + + .. versionchanged:: 0.16 + This method can be called from the observer thread. + """ + self.send_stop() + try: + self.join() + except RuntimeError: + pass diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt4.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt4.py new file mode 100644 index 0000000..dc1d56b --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt4.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyqt4 + ============ + + PyQt4 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt4\_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt4.QtCore` from PyQt4\_ must be available when importing this + module. + + .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt4 import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) + + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt5.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt5.py new file mode 100644 index 0000000..73ab057 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyqt5.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.pyqt5 + ============ + + PyQt5 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt5_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt5.QtCore` from PyQt5_ must be available when importing this + module. + + .. _gPyQt5: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Tobias Gehring +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt5 import QtCore + +from ._qt_base import MonitorObserverGenerator + + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyside.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyside.py new file mode 100644 index 0000000..b090fa8 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/pyside.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyside + ============= + + PySide integration. + + :class:`QUDevMonitorObserver` integrates device monitoring into the PySide\_ + mainloop by turing device events into Qt signals. + + :mod:`PySide.QtCore` from PySide\_ must be available when importing this + module. + + .. _PySide: http://www.pyside.org + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.6 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PySide import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/version.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/version.py new file mode 100644 index 0000000..43c1e96 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/version.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.version + ============== + + Version information. + + .. moduleauthor:: mulhern +""" + +__version_info__ = (0, 21, 0, '') +__version__ = "%s%s" % \ + ( + ".".join(str(x) for x in __version_info__[:3]), + "".join(str(x) for x in __version_info__[3:]) + ) diff --git a/deb_dist/multibootusb-8.5.0/scripts/pyudev/wx.py b/deb_dist/multibootusb-8.5.0/scripts/pyudev/wx.py new file mode 100644 index 0000000..8279dd4 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/pyudev/wx.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +"""pyudev.wx + ========= + + Wx integration. + + :class:`MonitorObserver` integrates device monitoring into the wxPython\_ + mainloop by turing device events into wx events. + + :mod:`wx` from wxPython\_ must be available when importing this module. + + .. _wxPython: http://wxpython.org/ + + .. moduleauthor:: Tobias Eberle + .. versionadded:: 0.14 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from wx import EvtHandler, PostEvent +from wx.lib.newevent import NewEvent + +import pyudev + + +DeviceEvent, EVT_DEVICE_EVENT = NewEvent() + + +class MonitorObserver(EvtHandler): + """ + An observer for device events integrating into the :mod:`wx` mainloop. + + This class inherits :class:`~wx.EvtHandler` to turn device events into + wx events: + + >>> from pyudev import Context, Monitor + >>> from pyudev.wx import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(event): + ... print('action {0} on device {1}'.format(event.device.action, event.device)) + >>> observer.Bind(EVT_DEVICE_EVENT, device_event) + >>> monitor.start() + + This class is a child of :class:`wx.EvtHandler`. + + .. versionadded:: 0.17 + """ + + def __init__(self, monitor): + EvtHandler.__init__(self) + self.monitor = monitor + self._observer_thread = None + self.start() + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + """ + return self._observer_thread is not None + + @enabled.setter + def enabled(self, value): + if value: + self.start() + else: + self.stop() + + def start(self): + """ + Enable this observer. + + Do nothing, if the observer is already enabled. + """ + if self._observer_thread is not None: + return + self._observer_thread = pyudev.MonitorObserver( + self.monitor, callback=self._emit_event, + name='wx-observer-thread') + self._observer_thread.start() + + def stop(self): + """ + Disable this observer. + + Do nothing, if the observer is already disabled. + """ + if self._observer_thread is None: + return + self._observer_thread.stop() + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(device=device)) + + +DeviceAddedEvent, EVT_DEVICE_ADDED = NewEvent() +DeviceRemovedEvent, EVT_DEVICE_REMOVED = NewEvent() +DeviceChangedEvent, EVT_DEVICE_CHANGED = NewEvent() +DeviceMovedEvent, EVT_DEVICE_MOVED = NewEvent() + + +class WxUDevMonitorObserver(MonitorObserver): + """An observer for device events integrating into the :mod:`wx` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_event_map = { + 'add': DeviceAddedEvent, + 'remove': DeviceRemovedEvent, + 'change': DeviceChangedEvent, + 'move': DeviceMovedEvent + } + + def __init__(self, monitor): + MonitorObserver.__init__(self, monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.wx.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(action=device.action, device=device)) + event_class = self._action_event_map.get(device.action) + if event_class is not None: + PostEvent(self, event_class(device=device)) diff --git a/deb_dist/multibootusb-8.5.0/scripts/qemu.py b/deb_dist/multibootusb-8.5.0/scripts/qemu.py new file mode 100644 index 0000000..467ba75 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/qemu.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: qemu.py +# Purpose: Module to boot ISO and USB disks using QEMU. +# Depends: QEMU must be installed under Linux for availing this feature. For windows, QEMU package is shipped +# along with executable file +# 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 .admin import adminCmd +from PyQt5 import QtWidgets +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * + + +class Qemu(QtWidgets.QDialog, Ui_Dialog): + """ + ISO and USB booting using QEMU. + """ + def on_Qemu_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_2.clear() + + qemu = self.check_qemu_exist() + + if not qemu is None: + + qemu_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + else: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu package to avail this feature.') + qemu_iso_link = None + + if not qemu_iso_link is None: + self.ui.lineEdit_2.insert(qemu_iso_link) + else: + log ("File not selected.") + + def on_Qemu_Boot_iso_Click(self): + """ + Main function to boot a selected ISO. + :return: + """ + if not self.ui.lineEdit_2.text(): + QtWidgets.QMessageBox.information(self, 'No ISO...', 'No ISO selected.\n\nPlease choose an iso and click Boot ISO.') + else: + qemu = self.check_qemu_exist() + qemu_iso_link = str(self.ui.lineEdit_2.text()) + if qemu is None: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_iso_ram() + if not ram is None: + self.ui.lineEdit_2.clear() + if platform.system() == "Windows": + try: + log("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Unable to start QEMU.') + else: + log(qemu + ' -m ' + ram + ' -cdrom ' + str(qemu_iso_link) + ' -boot d') + try: + log("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting ISO\n' + 'Unable to start QEMU.') + else: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot ISO.') + + + def on_Qemu_Boot_usb_Click(self, usb_disk): + """ + Main function to boot a selected USB disk. + :param usb_disk: Path to usb disk. + :return: + """ + qemu = self.check_qemu_exist() + + if qemu is None: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_usb_ram() + if ram is None: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot USB.') + else: + if platform.system() == "Windows": + disk_number = self.get_physical_disk_number(usb_disk) + parent_dir = os.getcwd() + os.chdir(resource_path(os.path.join("data", "tools", "qemu"))) + try: + log("Executing ==> " + qemu + " -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number) + subprocess.Popen("qemu-system-x86_64.exe -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n' + 'Unable to start QEMU.') + os.chdir(parent_dir) + elif platform.system() == "Linux": + try: + qemu_cmd = qemu + ' -hda ' + usb_disk[:-1] + ' -m ' + ram + ' -vga std' + log('Executing ==> ' + qemu_cmd) + # adminCmd([qemu, '-hda', usb_disk[:-1], '-m', ram, '-vga std'], gui=True) + subprocess.Popen(qemu_cmd, shell=True) + # adminCmd(qemu_cmd, gui=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n\nUnable to start QEMU.') + + def qemu_iso_ram(self): + """ + Choose a ram size for ISO booting. + :return: Ram size as string. + """ + if self.ui.ram_iso_256.isChecked(): + return str(256) + elif self.ui.ram_iso_512.isChecked(): + return str(512) + elif self.ui.ram_iso_768.isChecked(): + return str(768) + elif self.ui.ram_iso_1024.isChecked(): + return str(1024) + elif self.ui.ram_iso_2048.isChecked(): + return str(2047) + else: + return None + + def qemu_usb_ram(self): + """ + Choose a ram size for USB booting. + :return: Ram size as string. + """ + if self.ui.ram_usb_256.isChecked(): + return str(256) + if self.ui.ram_usb_512.isChecked(): + return str(512) + if self.ui.ram_usb_768.isChecked(): + return str(768) + if self.ui.ram_usb_1024.isChecked(): + return str(1024) + if self.ui.ram_usb_2048.isChecked(): + return str(2047) + else: + return None + + def check_qemu_exist(self): + """ + Check if QEMU is available on host system. + :return: path to QEMU program or None otherwise. + """ + if platform.system() == "Linux": + if subprocess.call('which qemu-system-x86_64', shell=True) == 0: + log("qemu-system-x86_64 exists...") + qemu = "qemu-system-x86_64" + elif subprocess.call('which qemu', shell=True) == 0: + log("qemu exists") + qemu = "qemu" + else: + qemu = None + + if qemu: + return qemu + else: + return None + elif platform.system() == "Windows": + log(resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe"))) + return resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe")) + + + def get_physical_disk_number(self, usb_disk): + """ + Get the physical disk number as detected ny Windows. + :param usb_disk: USB disk (Like F:) + :return: Disk number. + """ + import wmi + c = wmi.WMI () + for physical_disk in c.Win32_DiskDrive (): + for partition in physical_disk.associators ("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators ("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == usb_disk: + """ + log physical_disk.Caption + log partition.Caption + log logical_disk.Caption + """ + log("Physical Device Number is " + partition.Caption[6:-14]) + return str(partition.Caption[6:-14]) diff --git a/deb_dist/multibootusb-8.5.0/scripts/syslinux.py b/deb_dist/multibootusb-8.5.0/scripts/syslinux.py new file mode 100644 index 0000000..ff98fb1 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/syslinux.py @@ -0,0 +1,189 @@ +#!/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 sys +import subprocess +import platform +from .gen import * +from . import usb +from .iso import * +from . import config + +extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4") +syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4") +extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"] +syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"] +mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin")) + + +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 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 + + +def syslinux_default(usb_disk, version=4): + """ + 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_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin + ' of=' + usb_disk[:-1] + # log (usb_fs) + 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) + if subprocess.call(extlinu_cmd, shell=True) == 0: + log ("\nDefault Extlinux install is success...\n") + log ('\nExecuting ==> ' + mbr_install_cmd) + if subprocess.call(mbr_install_cmd, shell=True) == 0: + log ("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + + elif usb_fs in syslinux_fs: + + if platform.system() == "Linux": + 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 ==> " + syslinux_cmd + "\n") + if subprocess.call(syslinux_cmd, shell=True) == 0: + log ("\nDefault syslinux install is success...\n") + if subprocess.call(mbr_install_cmd, shell=True) == 0: + log ("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + else: + log ("\nFailed to install default syslinux...\n") + return False + + elif platform.system() == "Windows": + syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe")) + log ('Executing ==>' + syslinux + ' -maf -d multibootusb ' + usb_disk) + if subprocess.call(syslinux + ' -maf -d multibootusb ' + usb_disk, shell=True) == 0: + log ("\nDefault syslinux install is success...\n") + return True + else: + log ("\nFailed to install default syslinux...\n") + return False + + +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 distro == "generic" or distro == "alpine": + 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: + if config.syslinux_version == str(3): + if distro == "generic" and iso_linux_bin_dir == "/": + option = "" + else: + option = " -d " + else: + if distro == "generic" and iso_linux_bin_dir == "/": + option = " -i " + else: + option = " -i -d " + + if platform.system() == "Linux": + 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) == 0 + sys_cmd = syslinux_path + option + quote(distro_syslinux_install_dir) + ' ' + usb_disk + dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1' + log ("Executing ==> " + sys_cmd) + if subprocess.call(sys_cmd, shell=True) == 0: + log ("\nSyslinux install on distro directory is success...\n") + log ('Executing ==> ' + dd_cmd + '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + log ("\nBootsector copy is success...\n") + else: + log ("\nFailed to copy boot sector...\n") + else: + log ("\nFailed to install syslinux on distro directory...\n") + 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 + option + distro_syslinux_install_dir + ' ' + usb_disk + ' ' + \ + distro_sys_install_bs + log ("\nExecuting ==> " + sys_cmd, '\n') + if subprocess.call(sys_cmd, shell=True) == 0: + log ("\nSyslinux install was successful on distro directory...\n") + else: + 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=' + usb_mount + 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) == 0 + log ("Executing ==> " + ext_cmd) + if subprocess.call(ext_cmd, shell=True) == 0: + log ("\nSyslinux install on distro directory is success...\n") + log ('Executing ==> ' + dd_cmd, '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + log ("\nBootsector copy is success...\n") + else: + log ("\nFailed to install syslinux on distro directory...\n") + +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') diff --git a/deb_dist/multibootusb-8.5.0/scripts/udisks.py b/deb_dist/multibootusb-8.5.0/scripts/udisks.py new file mode 100644 index 0000000..659b62d --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/udisks.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +# Name: udisks.py +# Purpose: Module to mount unmount and eject using dbus and udisk +# Authors: Original author is Kovid Goyal and python3 +# supporte by Sundar for multibootusb project +# Licence: 'GPL v3' as per original Licence + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +# from __future__ import print_function +import os, re + + +def node_mountpoint(node): + + def de_mangle(raw): + return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012', + '\n').replace('\\0134', '\\') + + for line in open('/proc/mounts').readlines(): + line = line.split() + if line[0] == node: + return de_mangle(line[1]) + return None + + +class NoUDisks1(Exception): + pass + + +class UDisks(object): + + def __init__(self): + import dbus + self.bus = dbus.SystemBus() + try: + self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + '/org/freedesktop/UDisks'), 'org.freedesktop.UDisks') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown': + raise NoUDisks1() + raise + + def device(self, device_node_path): + import dbus + devpath = self.main.FindDeviceByDeviceFile(device_node_path) + return dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + devpath), 'org.freedesktop.UDisks.Device') + + def mount(self, device_node_path): + d = self.device(device_node_path) + try: + return str(d.FilesystemMount('', + ['auth_no_user_interaction', 'rw', 'noexec', 'nosuid', + 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()])) + except: + # May be already mounted, check + mp = node_mountpoint(str(device_node_path)) + if mp is None: + raise + return mp + + def unmount(self, device_node_path): + d = self.device(device_node_path) + d.FilesystemUnmount(['force']) + + def eject(self, device_node_path): + parent = device_node_path + while parent[-1] in '0123456789': + parent = parent[:-1] + d = self.device(parent) + d.DriveEject([]) + + +class NoUDisks2(Exception): + pass + + +class UDisks2(object): + + BLOCK = 'org.freedesktop.UDisks2.Block' + FILESYSTEM = 'org.freedesktop.UDisks2.Filesystem' + DRIVE = 'org.freedesktop.UDisks2.Drive' + + def __init__(self): + import dbus + self.bus = dbus.SystemBus() + try: + self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown': + raise NoUDisks2() + raise + + def device(self, device_node_path): + device_node_path = os.path.realpath(device_node_path) + devname = device_node_path.split('/')[-1] + + # First we try a direct object path + bd = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices/%s'%devname) + try: + device = bd.Get(self.BLOCK, 'Device', + dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + except: + device = None + + if device == device_node_path: + return bd + + # Enumerate all devices known to UDisks + devs = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices') + xml = devs.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable') + for dev in re.finditer(r'name=[\'"](.+?)[\'"]', type('')(xml)): + bd = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices/%s2'%dev.group(1)) + try: + device = bd.Get(self.BLOCK, 'Device', + dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + except: + device = None + if device == device_node_path: + return bd + + raise ValueError('%r not known to UDisks2'%device_node_path) + + def mount(self, device_node_path): + d = self.device(device_node_path) + mount_options = ['rw', 'noexec', 'nosuid', + 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()] + try: + return str(d.Mount( + { + 'auth.no_user_interaction':True, + 'options':','.join(mount_options) + }, + dbus_interface=self.FILESYSTEM)) + except: + # May be already mounted, check + mp = node_mountpoint(str(device_node_path)) + if mp is None: + raise + return mp + + def unmount(self, device_node_path): + d = self.device(device_node_path) + d.Unmount({'force':True, 'auth.no_user_interaction':True}, + dbus_interface=self.FILESYSTEM) + + def drive_for_device(self, device): + drive = device.Get(self.BLOCK, 'Drive', + dbus_interface='org.freedesktop.DBus.Properties') + return self.bus.get_object('org.freedesktop.UDisks2', drive) + + def eject(self, device_node_path): + drive = self.drive_for_device(self.device(device_node_path)) + drive.Eject({'auth.no_user_interaction':True}, + dbus_interface=self.DRIVE) + + +def get_udisks(ver=None): + if ver is None: + try: + u = UDisks2() + except NoUDisks2: + u = UDisks() + return u + return UDisks2() if ver == 2 else UDisks() + + +def get_udisks1(): + u = None + try: + u = UDisks() + except NoUDisks1: + try: + u = UDisks2() + except NoUDisks2: + pass + if u is None: + raise EnvironmentError('UDisks not available on your system') + return u + + +def mount(node_path): + u = get_udisks1() + u.mount(node_path) + + +def eject(node_path): + u = get_udisks1() + u.eject(node_path) + + +def umount(node_path): + u = get_udisks1() + u.unmount(node_path) + + +def test_udisks(ver=None): + import sys + dev = sys.argv[1] + print('Testing with node', dev) + u = get_udisks(ver=ver) + print('Using Udisks:', u.__class__.__name__) + print('Mounted at:', u.mount(dev)) + print('Unmounting') + u.unmount(dev) + print('Mounting') + u.mount(dev) + print('Ejecting:') + u.eject(dev) + +if __name__ == '__main__': + print('Run test here...') + # test_udisks() diff --git a/deb_dist/multibootusb-8.5.0/scripts/uninstall_distro.py b/deb_dist/multibootusb-8.5.0/scripts/uninstall_distro.py new file mode 100644 index 0000000..2dcf9fe --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/uninstall_distro.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: uninstall_distro.py +# Purpose: Module to uninstall distros installed by multibootusb +# 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 re +import shutil +import threading +import platform +from .usb import * +from . import config +from . import gen + + +def install_distro_list(): + """ + List all distro names installed by previous install + :return: List of distro names as list + """ + usb_details = details(config.usb_disk) + config.usb_mount = usb_details['mount_point'] + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + + if os.path.exists(sys_cfg_file): + distro_list = [] + for line in open(sys_cfg_file): + if "#start " in line: + distro_list.append(line[7:]) + return distro_list + else: + return None + + +def unin_distro(): + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + config.uninstall_distro_dir_name = config.uninstall_distro_dir_name.replace('\n', '') + gen.log(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + if config.distro: + uninstall_distro() + else: + return "" + + +def delete_frm_file_list(): + """ + Generic way to remove files from USB disk. + :param config.usb_disk: + :param iso_file_list: List of files installed in the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if config.iso_file_list is not None: + for f in config.iso_file_list: + if platform.system() == "Windows": + f = f.replace('\n', '').strip("/").replace("/", "\\") + else: + f = f.replace('\n', '').strip("/") + if os.path.exists(os.path.join(usb_mount, "ldlinux.sys")): + os.chmod(os.path.join(usb_mount, "ldlinux.sys"), 0o777) + os.unlink(os.path.join(usb_mount, "ldlinux.sys")) + + if os.path.exists(os.path.join(usb_mount, f)): + gen.log("Removing " + (os.path.join(usb_mount, f))) + if os.path.isfile(os.path.join(usb_mount, f)): + os.remove(os.path.join(usb_mount, f)) + elif os.path.isdir(os.path.join(usb_mount, f)): + shutil.rmtree(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg"), "r") as generic_cfg: + if platform.system() == "Windows": + generic = generic_cfg.read().replace('\n', '').replace("/", "\\") + else: + generic = generic_cfg.read().replace('\n', '') + if os.path.exists(os.path.join(usb_mount, generic.strip("/"))): + os.remove(os.path.join(usb_mount, generic.strip("/"))) + if platform.system() == 'Linux': + gen.log('Removed files from' + config.uninstall_distro_dir_name) + gen.log('Syncing....') + os.system('sync') + + + + +def uninstall_distro(): + """ + Uninstall selected distro from selected USB disk. + :param config.usb_disk: Path of the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :param _distro: Generic name applied to distro install by multibootusb + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg"), "r") as f: + config.iso_file_list = f.readlines() + # gen.log iso_file_list + + for path, subdirs, files in os.walk(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + for name in files: + if name.endswith('ldlinux.sys') or name.endswith('ldlinux.c32'): + os.chmod(os.path.join(path, name), 0o777) + os.unlink(os.path.join(path, name)) + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")): + os.remove(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")) + elif config.distro == "windows" or config.distro == "alpine" or config.distro == "generic": + delete_frm_file_list() + if config.distro == "ipfire": + files = os.listdir(usb_mount) + for f in files: + if f.endswith('.tlz'): + os.remove(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "distro.img")): + os.remove(os.path.join(usb_mount, "distro.img")) + elif config.distro == "trinity-rescue": + shutil.rmtree(os.path.join(usb_mount, "trk3")) + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + if platform.system() == 'Linux': + os.system('sync') + shutil.rmtree(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)) + + delete_frm_file_list() + + update_sys_cfg_file() + + +def update_sys_cfg_file(): + """ + Main function to remove uninstall distro specific operations. + :return: + """ + if platform.system() == 'Linux': + os.system('sync') + + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + if not os.path.exists(sys_cfg_file): + gen.log("syslinux.cfg file not found for updating changes.") + else: + gen.log("Updating syslinux.cfg file...") + string = open(sys_cfg_file).read() + string = re.sub(r'#start ' + config.uninstall_distro_dir_name + '.*?' + '#end ' + config.uninstall_distro_dir_name + '\s*', '', string, flags=re.DOTALL) + config_file = open(sys_cfg_file, "w") + config_file.write(string) + config_file.close() + + +def uninstall_progress(): + """ + Calculate uninstall progress percentage. + :return: + """ + from . import progressbar + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), + "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + else: + config.distro = "" + gen.log("Installed distro type is " + config.distro) + + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso"): + folder_size_to_remove = os.path.getsize(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso") + else: + folder_size_to_remove = 0 + folder_size_to_remove += disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + elif config.distro == "windows" or config.distro == "Windows": + if os.path.exists(os.path.join(usb_mount, "SOURCES")): + folder_size_to_remove = disk_usage(str(usb_mount) + "/SOURCES").used + else: + folder_size_to_remove = disk_usage(str(usb_mount) + "/SSTR").used + elif config.distro == "ipfire": + folder_size_to_remove = disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + files = os.listdir(os.path.join(str(usb_mount))) + for f in files: + if f.endswith('.tlz'): + folder_size_to_remove += os.path.getsize(os.path.join(config.usb_mount, f)) + elif config.distro == "trinity-rescue": + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "trk3")).used + folder_size_to_remove += disk_usage(usb_mount + "/multibootusb/" + config.uninstall_distro_dir_name).used + else: + + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)).used + + thrd = threading.Thread(target=unin_distro, name="uninstall_progress") + initial_usb_size = disk_usage(usb_mount).used + thrd.start() + config.status_text = "Uninstalling " + config.uninstall_distro_dir_name + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = disk_usage(usb_mount).used + diff_size = int(initial_usb_size - current_size) + config.percentage = round(float(diff_size) / folder_size_to_remove * 100) + if config.percentage > 100: + config.percentage = 100 + + pbar.update(config.percentage) + + if not thrd.is_alive(): + config.persistence = 0 + config.status_text = "" diff --git a/deb_dist/multibootusb-8.5.0/scripts/update_cfg_file.py b/deb_dist/multibootusb-8.5.0/scripts/update_cfg_file.py new file mode 100644 index 0000000..652efc6 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/update_cfg_file.py @@ -0,0 +1,383 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: update_cfg_file.py +# Purpose: Module to manipulate distro specific and main config files. +# 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 re +import shutil +from .usb import * +from .gen import * +from .iso import * +from . import config + + +def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0): + """ + Main function to modify/update distro specific strings on distro config files. + :return: + """ + usb_details = details(usb_disk) + usb_mount = usb_details['mount_point'] + usb_uuid = usb_details['uuid'] + usb_label = usb_details['label'] + patch = None + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + if isolinux_bin_exist(config.iso_link): + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(iso_link)[1:]) + config.status_text = "Updating config files..." + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + log('Updating distro specific config files...') + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith(".cfg") or f.endswith('.CFG') or f.endswith('.lst'): + cfg_file = os.path.join(dirpath, f) + try: + string = open(cfg_file, errors='ignore').read() + except IOError: + log("Unable to read ", cfg_file) + else: + if not distro == "generic": + replace_text = r'\1/multibootusb/' + iso_basename(iso_link) + '/' + string = re.sub(r'([ \t =,])/', replace_text, string) + if distro == "ubuntu": + string = re.sub(r'boot=casper', + 'boot=casper cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ' + 'ignore_bootid root=UUID=' + usb_uuid + ' live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/casper', string) + string = re.sub(r'ui gfxboot', '#ui gfxboot', string) + if not persistence == 0: + string = re.sub(r'boot=casper', 'boot=casper persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "debian" or distro == "debian-install": + string = re.sub(r'boot=live', 'boot=live ignore_bootid live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/live', string) + if not persistence == 0: + string = re.sub(r'boot=live', 'boot=live persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "ubuntu-server": + string = re.sub(r'file', + 'cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ignore_bootid root=UUID=' + + usb_uuid + ' file', string) + elif distro == "fedora": + string = re.sub(r'root=\S*', 'root=live:UUID=' + usb_uuid, string) + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', 'liveimg live_dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rd.live.dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + elif re.search(r'Solus', string, re.I): + string = re.sub(r'initrd=', 'rd.live.dir=/multibootusb/' + iso_basename(iso_link) + + '/LiveOS initrd=', string) + + if not persistence == 0: + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', 'liveimg overlay=UUID=' + usb_uuid, string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rw rd.live.overlay=UUID=' + usb_uuid, string) + string = re.sub(r' ro ', '', string) + elif distro == 'kaspersky': + if not os.path.exists(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')): + shutil.copyfile(resource_path(os.path.join('data', 'multibootusb', 'syslinux.cfg')), + os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')) + config_string = kaspersky_config('kaspersky') + config_string = config_string.replace('$INSTALL_DIR', '/multibootusb/' + iso_basename(iso_link)) + config_string = re.sub(r'root=live:UUID=', 'root=live:UUID=' + usb_uuid, config_string) + with open(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg'), "a") as f: + f.write(config_string) + elif distro == "parted-magic": + if re.search(r'append', string, re.I): + string = re.sub(r'append', 'append directory=/multibootusb/' + iso_basename(iso_link), string, + flags=re.I) + string = re.sub(r'initrd=', 'directory=/multibootusb/' + iso_basename(iso_link) + '/ initrd=', + string) + elif distro == "ubcd": + string = re.sub(r'iso_filename=\S*', 'directory=/multibootusb/' + iso_basename(iso_link), + string, flags=re.I) + elif distro == 'f4ubcd': + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + if not 'multibootusb' in string: + string = re.sub(r'/F4UBCD', '/multibootusb/' + iso_basename(iso_link) + '/F4UBCD', string) + elif distro == "ipcop": + string = re.sub(r'ipcopboot=cdrom\S*', 'ipcopboot=usb', string) + elif distro == "puppy": + string = re.sub(r'pmedia=cd\S*', + 'pmedia=usbflash psubok=TRUE psubdir=/multibootusb/' + iso_basename(iso_link) + '/', + string) + elif distro == "slax": + string = re.sub(r'initrd=', + r'from=/multibootusb/' + iso_basename(iso_link) + '/slax fromusb initrd=', string) + elif distro == "knoppix": + string = re.sub(r'initrd=', 'knoppix_dir=/multibootusb/' + iso_basename(iso_link) + '/KNOPPIX initrd=', string) + #string = re.sub(r'(append)', + # r'\1 knoppix_dir=/multibootusb/' + iso_basename(iso_link) + '/KNOPPIX', + # string) + elif distro == "gentoo": + string = re.sub(r'append ', + 'append real_root=' + usb_disk + ' slowusb subdir=/multibootusb/' + + iso_basename(iso_link) + '/ ', string, flags=re.I) + elif distro == "systemrescuecd": + rows = [] + subdir = '/multibootusb/' + iso_basename(iso_link) + '/' + for line in string.splitlines(True): + addline = True + if re.match(r'append.*--.*', line, flags=re.I): + line = re.sub(r'(append)(.*)--(.*)', r'\1\2subdir=' + subdir + r' --\3 subdir=' + subdir, + line, flags=re.I) + elif re.match(r'append', line, flags=re.I): + line = re.sub(r'(append)', r'\1 subdir=' + subdir, line, flags=re.I) + elif re.match(r'label rescue(32|64)_1', line, flags=re.I): + rows.append(line) + rows.append('append subdir=%s\n' % (subdir,)) + addline = False + + if addline: + rows.append(line) + + string = ''.join(rows) + elif distro == "arch" or distro == "chakra": + string = re.sub(r'isolabel=\S*', + 'isodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I) + string = re.sub(r'isobasedir=', + 'isobasedir=/multibootusb/' + iso_basename(iso_link) + '/', string, flags=re.I) + string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package + if 'manjaro' in string: + if not os.path.exists(os.path.join(usb_mount, '.miso')): + with open(os.path.join(usb_mount, '.miso'), "w") as f: + f.write('') + elif distro == "kaos": + string = re.sub(r'kdeosisolabel=\S*', + 'kdeosisodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I) + string = re.sub(r'append', + 'append kdeosisobasedir=/multibootusb/' + iso_basename(iso_link) + '/kdeos/', string, flags=re.I) + string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package + elif distro == "suse" or distro == "opensuse": + if re.search(r'opensuse_12', string, re.I): + string = re.sub(r'append', + 'append loader=syslinux isofrom_system=/dev/disk/by-uuid/' + usb_uuid + ":/" + + iso_name(iso_link), string, flags=re.I) + else: + string = re.sub(r'append', + 'append loader=syslinux isofrom_device=/dev/disk/by-uuid/' + usb_uuid + + ' isofrom_system=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string, flags=re.I) + elif distro == "pclinuxos": + string = re.sub(r'livecd=', + 'fromusb livecd=' + '/multibootusb/' + iso_basename(iso_link) + '/', + string) + string = re.sub(r'prompt', '#prompt', string) + string = re.sub(r'ui gfxboot.com', '#ui gfxboot.com', string) + string = re.sub(r'timeout', '#timeout', string) + elif distro == "wifislax": + string = re.sub(r'vmlinuz', + 'vmlinuz from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + string = re.sub(r'vmlinuz2', + 'vmlinuz2 from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + elif distro == "porteus": + string = re.sub(r'APPEND', + 'APPEND from=/multibootusb/' + iso_basename(iso_link) + ' noauto', string) + string = re.sub(r'vmlinuz2', + 'vmlinuz2 from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + elif distro == "hbcd": + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + elif distro == "zenwalk": + string = re.sub(r'initrd=', + 'from=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link) + ' initrd=', + string) + elif distro == "mageialive": + string = re.sub(r'LABEL=\S*', 'LABEL=' + usb_label, string) + elif distro == "antix": + string = re.sub(r'APPEND', 'image_dir=/multibootusb/' + iso_basename(iso_link), string) + elif distro == "solydx": + string = re.sub(r'live-media-path=', 'live-media-path=/multibootusb/' + iso_basename(iso_link), + string) + elif distro == "salix-live": + string = re.sub(r'iso_path', '/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string) + #string = re.sub(r'initrd', 'from=/multibootusb/' + iso_basename(iso_link) + '/' + ' initrd', string) + elif distro == 'alt-linux': + string = re.sub(r':cdrom', ':disk', string) + elif distro == 'fsecure': + string = re.sub(r'APPEND ramdisk_size', 'APPEND noprompt ' + 'knoppix_dir=/multibootusb/' + iso_basename(iso_link) + + '/KNOPPIX ramdisk_size', string) + + config_file = open(cfg_file, "w") + config_file.write(string) + config_file.close() + + update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro) + + +def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro): + """ + Update main multibootusb suslinux.cfg file after distro is installed. + :return: + """ + log('Updating multibootusb config file...') + sys_cfg_file = os.path.join(usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + if os.path.exists(sys_cfg_file): + + if distro == "hbcd": + if os.path.exists(os.path.join(usb_mount, "multibootusb", "menu.lst")): + _config_file = os.path.join(usb_mount, "multibootusb", "menu.lst") + config_file = open(_config_file,"w") + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', _config_file) + config_file.write(string) + config_file.close() + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") + elif distro == "Windows": + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL chain.c32 hd0 1 ntldr=/bootmgr" + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'f4ubcd': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL grub.exe" + "\n") + config_file.write('APPEND --config-file=/multibootusb/' + iso_basename(config.iso_link) + '/menu.lst' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'kaspersky': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("CONFIG " + '/multibootusb/' + iso_basename(config.iso_link) + '/kaspersky.cfg' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'grub4dos': + update_menu_lst() + elif distro == 'grub4dos_iso': + update_grub4dos_iso_menu() + else: + # admin.adminCmd(["mount", "-o", "remount,rw", config.usb_disk]) + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + if distro == "salix-live": + if os.path.exists(os.path.join(config.usb_mount, 'multibootusb', iso_basename(iso_link), 'boot', 'grub2-linux.img')): + config_file.write( + "LINUX " + '/multibootusb/' + iso_basename(iso_link) + '/boot/grub2-linux.img' + "\n") + else: + config_file.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n") + elif distro == "pclinuxos": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/isolinux/vmlinuz' + "\n") + config_file.write("append livecd=livecd root=/dev/rd/3 acpi=on vga=788 keyb=us vmalloc=256M nokmsboot " + "fromusb root=UUID=" + usb_uuid + " bootfromiso=/multibootusb/" + + iso_basename(iso_link) + "/" + iso_name(iso_link) + " initrd=/multibootusb/" + + iso_basename(iso_link) + '/isolinux/initrd.gz' + "\n") + elif distro == "mentest": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/BOOT/MEMTEST.IMG\n') + + elif distro == "sgrubd2": + config_file.write("LINUX memdisk\n") + config_file.write("INITRD " + "/multibootusb/" + iso_basename(iso_link) + '/' + iso_name(iso_link) + '\n') + config_file.write("APPEND iso\n") + elif distro == 'ReactOS': + config_file.write("COM32 mboot.c32" + '\n') + config_file.write("APPEND /loader/setupldr.sys" + '\n') + elif distro == 'pc-unlocker': + config_file.write("kernel ../ldntldr" + '\n') + config_file.write("append initrd=../ntldr" + '\n') + else: + if isolinux_bin_exist(config.iso_link) is True: + if distro == "generic": + distro_syslinux_install_dir = isolinux_bin_dir(iso_link) + if not isolinux_bin_dir(iso_link) == "/": + distro_sys_install_bs = os.path.join(usb_mount, isolinux_bin_dir(iso_link)) + '/' + distro + '.bs' + else: + distro_sys_install_bs = '/' + distro + '.bs' + else: + distro_syslinux_install_dir = install_dir + distro_syslinux_install_dir = distro_syslinux_install_dir.replace(usb_mount, '') + distro_sys_install_bs = distro_syslinux_install_dir + '/' + isolinux_bin_dir(iso_link) + '/' + distro + '.bs' + + distro_sys_install_bs = "/" + distro_sys_install_bs.replace("\\", "/") # Windows path issue. + + if config.syslinux_version == '3': + config_file.write("CONFIG /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/isolinux.cfg\n') + config_file.write("APPEND /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '\n') + else: + config_file.write("BOOT " + distro_sys_install_bs.replace("//", "/") + "\n") + + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith("isolinux.cfg") or f.endswith("ISOLINUX.CFG"): + if not os.path.exists(os.path.join(dirpath, "syslinux.cfg")) or not os.path.exists(os.path.join(dirpath, "SYSLINUX.CFG")): + shutil.copy2(os.path.join(dirpath, f), os.path.join(dirpath, "syslinux.cfg")) + + +def kaspersky_config(distro): + if distro == 'kaspersky': + return """ +menu label Kaspersky Rescue Disk + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ looptype=squashfs rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz lang=en udev liveimg splash quiet doscsi nomodeset +label text + menu label Kaspersky Rescue Disk - Text Mode + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=/multiboot/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet nox shell noresume doscsi nomodeset +label hwinfo + menu label Kaspersky Hardware Info + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet softlevel=boot nox hwinfo noresume doscsi nomodeset """ + + +def update_menu_lst(): + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + menu_lst = iso_menu_lst_path(config.iso_link).replace("\\", "/") + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("KERNEL grub.exe" + "\n") + f.write('APPEND --config-file=/' + menu_lst + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") + +def update_grub4dos_iso_menu(): + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + menu_lst_file = os.path.join(install_dir, 'menu.lst') + with open(menu_lst_file, "w") as f: + f.write("title Boot " + iso_name(config.iso_link) + "\n") + f.write("find --set-root --ignore-floppies --ignore-cd /multibootusb/" + iso_basename(config.iso_link) + '/' + + iso_name(config.iso_link) + "\n") + f.write("map --heads=0 --sectors-per-track=0 /multibootusb/" + iso_basename(config.iso_link) + + '/' + iso_name(config.iso_link) + ' (hd32)' + "\n") + f.write("map --hook" + "\n") + f.write("chainloader (hd32)") + + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("KERNEL grub.exe" + "\n") + f.write('APPEND --config-file=/multibootusb/' + iso_basename(config.iso_link) + '/' + iso_name(config.iso_link) + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") diff --git a/deb_dist/multibootusb-8.5.0/scripts/usb.py b/deb_dist/multibootusb-8.5.0/scripts/usb.py new file mode 100644 index 0000000..9c27c8b --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/scripts/usb.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: usb.py +# Purpose: Module to list USB devices and get details under Linux and Windows +# 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 sys +import platform +import os +import shutil +import collections +import ctypes +import subprocess +from . import gen +if platform.system() == 'Linux': + from . import udisks + u = udisks.get_udisks(ver=None) +if platform.system() == 'Windows': + import psutil + import win32com.client + import win32com.client + import wmi + import pythoncom + + +def is_block(usb_disk): + """ + Function to detect if the USB is block device + :param usb_disk: USB disk path + :return: True is devie is block device else False + """ + import stat + if platform.system() == 'Linux': + if len(usb_disk) != 9: + return False + elif platform.system() == 'Windows': + if len(usb_disk) != 2: + return False + else: + return True + try: + mode = os.stat(usb_disk).st_mode + gen.log(mode) + gen.log(stat.S_ISBLK(mode)) + except: + return False + + return stat.S_ISBLK(mode) + + +def disk_usage(mount_path): + """ + Return disk usage statistics about the given path as a (total, used, free) + namedtuple. Values are expressed in bytes. + """ + # Author: Giampaolo Rodola' + # License: MIT + _ntuple_diskusage = collections.namedtuple('usage', 'total used free') + + if platform.system() == "Linux": + st = os.statvfs(mount_path) + free = st.f_bavail * st.f_frsize + total = st.f_blocks * st.f_frsize + used = (st.f_blocks - st.f_bfree) * st.f_frsize + + return _ntuple_diskusage(total, used, free) + + elif platform.system() == "Windows": + + _, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), \ + ctypes.c_ulonglong() + if sys.version_info >= (3,) or isinstance(mount_path, unicode): + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExW + else: + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExA + ret = fun(mount_path, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free)) + if ret == 0: + raise ctypes.WinError() + used = total.value - free.value + + return _ntuple_diskusage(total.value, used, free.value) + else: + raise NotImplementedError("Platform not supported.") + + +def list(partition=1, fixed=None): + """ + List inserted USB devices. + :return: USB devices as list. + """ + devices = [] + if platform.system() == "Linux": + from . import pyudev + import dbus + + + try: + # pyudev is good enough to detect USB devices on modern Linux machines. + gen.log("Using pyudev for detecting USB drives...") + context = pyudev.Context() + if fixed is None: + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + # if device['ID_BUS'] == "usb" and device['DEVTYPE'] == "partition": + if device.get('ID_BUS') in ("usb", "scsi") and device.get('DEVTYPE') == "partition": + # gen.log(device['DEVNAME']) + devices.append(str(device['DEVNAME'])) + else: + for device in context.list_devices(subsystem='block', DEVTYPE='partition'): + devices.append(str(device['DEVNAME'])) + except: + bus = dbus.SystemBus() + try: + # You should come here only if your system does'nt have udev installed. + # We will use udiskd2 for now. + gen.log("Falling back to Udisks2..") + ud_manager_obj = bus.get_object( + 'org.freedesktop.UDisks2', '/org/freedesktop/UDisks2') + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.DBus.ObjectManager') + for k, v in ud_manager.GetManagedObjects().iteritems(): + drive_info = v.get('org.freedesktop.UDisks2.Block', {}) + if drive_info.get('IdUsage') == "filesystem" and not drive_info.get( + 'HintSystem') and not drive_info.get('ReadOnly'): + device = drive_info.get('Device') + device = bytearray(device).replace( + b'\x00', b'').decode('utf-8') + devices.append(device) + except: + try: + # You must be using really old distro. Otherwise, the code + # should not reach here. + gen.log("Falling back to Udisks1...") + ud_manager_obj = bus.get_object( + "org.freedesktop.UDisks", "/org/freedesktop/UDisks") + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.UDisks') + for dev in ud_manager.EnumerateDevices(): + device_obj = bus.get_object( + "org.freedesktop.UDisks", dev) + device_props = dbus.Interface( + device_obj, dbus.PROPERTIES_IFACE) + if device_props.Get('org.freedesktop.UDisks.Device', + "DriveConnectionInterface") == "usb" and device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceIsPartition"): + if device_props.Get('org.freedesktop.UDisks.Device', "DeviceIsMounted"): + device_file = device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceFile") + devices.append(device_file) + except: + gen.log("No USB device found...") + + elif platform.system() == "Windows": + if fixed is not None: + for drive in psutil.disk_partitions(): + if 'cdrom' in drive.opts or drive.fstype == '': + # Skip cdrom drives or the disk with no filesystem + continue + devices.append(drive[0][:-1]) + else: + try: + # Try new method using psutil. It should also detect USB 3.0 (but not tested by me) + for drive in psutil.disk_partitions(): + if 'cdrom' in drive.opts or drive.fstype == '': + # Skip cdrom drives or the disk with no filesystem + continue + if 'removable' in drive.opts: + devices.append(drive[0][:-1]) + except: + # Revert back to old method if psutil fails (which is unlikely) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + devices.append(drive) + + if devices: + return devices + else: + gen.log("No USB device found...") + return None + + +def details_udev(usb_disk_part): + """ + Get details of USB partition using udev + """ + if platform.system() == "Linux": + from . import pyudev + """ + Try with PyUdev to get the details of USB disks. + This is the easiest and reliable method to find USB details. + Also, it is a standalone package and no dependencies are required. + """ + # gen.log "Using PyUdev for detecting USB details..." + context = pyudev.Context() + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + fdisk_cmd_out = subprocess.check_output('fdisk -l ' + usb_disk_part, shell=True) + if b'Extended' in fdisk_cmd_out: + mount_point = '' + uuid = 'No_UUID' + file_system = 'No_FS' + vendor = 'No_Vendor' + model = 'No_Model' + label = 'No_Label' + elif device.get('ID_BUS') in ("usb", "scsi") and device.get('DEVTYPE') == "partition": + if (device['DEVNAME']) == usb_disk_part: + uuid = str(device['ID_FS_UUID']) + file_system = str(device['ID_FS_TYPE']) + try: + label = str(device['ID_FS_LABEL']) + except: + label = "No_Label" + mount_point = u.mount(usb_disk_part) + # mount_point = os.popen('findmnt -nr -o target -S %s' % usb_disk_part).read().strip() + # Convert the hex string of space to empty space. + mount_point = mount_point.replace('\\x20', ' ') + try: + vendor = str(device['ID_VENDOR']) + except: + vendor = str('No_Vendor') + try: + model = str(device['ID_MODEL']) + except: + model = str('No_Model') + + if not mount_point == '': + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + mount_point = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details_udisks2(usb_disk_part): + """ + Get details of USB disk detail. + usb_disk_part: It is the partition of an USB removable disk. + """ + import dbus + bus = dbus.SystemBus() + bd = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2/block_devices%s'%usb_disk_part[4:]) + device = bd.Get('org.freedesktop.UDisks2.Block', 'Device', dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + uuid = bd.Get('org.freedesktop.UDisks2.Block', 'IdUUID', dbus_interface='org.freedesktop.DBus.Properties') + file_system = bd.Get('org.freedesktop.UDisks2.Block', 'IdType', dbus_interface='org.freedesktop.DBus.Properties') + mount_point = bd.Get('org.freedesktop.UDisks2.Filesystem', 'MountPoints', dbus_interface='org.freedesktop.DBus.Properties') + if mount_point: + # mount_point = str(bytearray(mount_point[0]).decode('utf-8').replace(b'\x00', b'')) + mount_point = bytearray(mount_point[0]).replace(b'\x00', b'').decode('utf-8') + else: + try: + mount_point = u.mount(usb_disk_part) + except: + mount_point = "No_Mount" + try: + label = bd.Get('org.freedesktop.UDisks2.Block', 'IdLabel', dbus_interface='org.freedesktop.DBus.Properties') + except: + label = "No_Label" + usb_drive_id = (bd.Get('org.freedesktop.UDisks2.Block', 'Drive', dbus_interface='org.freedesktop.DBus.Properties')) + bd1 = bus.get_object('org.freedesktop.UDisks2', usb_drive_id) + try: + vendor = bd1.Get('org.freedesktop.UDisks2.Drive', 'Vendor', dbus_interface='org.freedesktop.DBus.Properties') + except: + vendor = str('No_Vendor') + try: + model = bd1.Get('org.freedesktop.UDisks2.Drive', 'Model', dbus_interface='org.freedesktop.DBus.Properties') + except: + model = str('No_Model') + if not mount_point == "No_Mount": + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def bytes2human(n): + """ + Convert the size to human readable format + Authored by 'Giampaolo Rodolà' and original link is:- + http://code.activestate.com/recipes/577972-disk-usage/ + """ + try: + n = int(n) + except: + return 'Unknown' + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = {} + for i, s in enumerate(symbols): + prefix[s] = 1 << (i + 1) * 10 + for s in reversed(symbols): + if n >= prefix[s]: + value = float(n) / prefix[s] + return '%.1f%s' % (value, s) + return "%sB" % n + + +def win_disk_details(disk_drive): + """ + Populate and get details of an USB disk under windows. Minimum required windows version is Vista. + :param disk_drive: USB disk like 'G:' + :return: See the details(usb_disk_part) function for return values. + """ + pythoncom.CoInitialize() + vendor = 'Not_Found' + model = 'Not_Found' + selected_usb_part = str(disk_drive) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No_label" + mount_point = selected_usb_device + ":\\" + serno = "%X" % (int(d.SerialNumber) & 0xFFFFFFFF) + uuid = serno[:4] + '-' + serno[4:] + file_system = (d.FileSystem).strip() + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + ''' + # The below code works only from vista and above. I have removed it as many people reported that the software + # was not working under windows xp. Even then, it is significantly slow if 'All Drives' option is checked. + # Removing the code doesn't affect the functionality as it is only used to find vendor id and model of the drive. + c = wmi.WMI() + for physical_disk in c.Win32_DiskDrive(InterfaceType="USB"): + for partition in physical_disk.associators("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == disk_drive: + vendor = (physical_disk.PNPDeviceID.split('&VEN_'))[1].split('&PROD_')[0] + model = (physical_disk.PNPDeviceID.split('&PROD_'))[1].split('&REV_')[0] + ''' + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details(usb_disk_part): + """ + Populate and get details of an USB disk. + :param usb_disk_part: USB disk. Example.. "/dev/sdb1" on Linux and "D:\" on Windows. + :return: label == > returns name/label of an inserted USB device. + mount_point == > returns mount path of an inserted USB device. + uuid == > returns uuid of an inserted USB device. + file_system == > returns type of filesystem of an inserted USB device. + device == > returns device path of an inserted USB device. + size_total == > returns total size in MB/GB of an inserted USB device. + size_free == > returns free size in MB/GB of an inserted USB device. + size_used == > returns used size in MB/GB of an inserted USB device. + vendor == > returns the name of the manufacturer. + model == > returns the model name of the USB. + """ + if platform.system() == 'Linux': + try: + udev = details_udev(usb_disk_part) + uuid = udev['uuid'] + file_system = udev['file_system'] + label = udev['label'] + mount_point = udev['mount_point'] + size_total = udev['size_total'] + size_used = udev['size_used'] + size_free = udev['size_free'] + vendor = udev['vendor'] + model = udev['model'] + except: + udisks2 = details_udisks2(usb_disk_part) + uuid = udisks2['uuid'] + file_system = udisks2['file_system'] + label = udisks2['label'] + mount_point = udisks2['mount_point'] + size_total = udisks2['size_total'] + size_used = udisks2['size_used'] + size_free = udisks2['size_free'] + vendor = udisks2['vendor'] + model = udisks2['model'] + elif platform.system() == 'Windows': + win_details = win_disk_details(usb_disk_part) + uuid = win_details['uuid'] + file_system = win_details['file_system'] + label = win_details['label'] + mount_point = win_details['mount_point'] + size_total = win_details['size_total'] + size_used = win_details['size_used'] + size_free = win_details['size_free'] + vendor = win_details['vendor'] + model = win_details['model'] + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + +if __name__ == '__main__': + usb_devices = list() + if usb_devices is not None: + for dev in usb_devices: + gen.log(details(dev)) diff --git a/deb_dist/multibootusb-8.5.0/setup.py b/deb_dist/multibootusb-8.5.0/setup.py new file mode 100644 index 0000000..a399520 --- /dev/null +++ b/deb_dist/multibootusb-8.5.0/setup.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Name: setup.py +# Purpose: Module to create packages or install multibootusb package from source +# 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 + +from distutils.core import setup +#from setuptools import setup, find_packages +import os +import sys +from scripts.gen import mbusb_version + +Version = mbusb_version() +print(Version) +setup( + name='multibootusb', + version=Version, + packages=['scripts', 'scripts.pyudev', 'scripts.pyudev.device', 'scripts.pyudev._ctypeslib', 'scripts.pyudev._os', + 'scripts.gui', 'scripts.progressbar'], + #packages=find_packages(), + scripts=['multibootusb', 'multibootusb-pkexec'], + platforms=['Linux'], + url='http://multibootusb.org/', + license='General Public License (GPL)', + author='Sundar', + author_email='feedback.multibootusb@gmail.com', + description='Create multi boot live Linux on a USB disk...', + long_description='multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives.', + data_files=[("/usr/share/applications", ["data/multibootusb.desktop"]), + ('/usr/share/pixmaps', ["data/tools/multibootusb.png"]), + ('/usr/share/polkit-1/actions/', ['org.debian.pkexec.run-multibootusb.policy']), + ('/usr/share/multibootusb/data/tools', ["data/tools/mbr.bin"]), + ('/usr/share/multibootusb/data', ["data/version.txt"]), + ('/usr/share/multibootusb/data/tools', ["data/tools/multibootusb.png"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/dd.exe"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/diskio.dll"]), + ('/usr/share/multibootusb/data/tools/mkfs', ["data/tools/mkfs/mke2fs.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/chain.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/bg.png"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/extlinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/grub.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/memdisk"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.lst"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/syslinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/vesamenu.c32"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_modules.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux_64.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_windows.zip"])] +) diff --git a/deb_dist/multibootusb_8.5.0-1.debian.tar.xz b/deb_dist/multibootusb_8.5.0-1.debian.tar.xz new file mode 100644 index 0000000..4f8a0c5 Binary files /dev/null and b/deb_dist/multibootusb_8.5.0-1.debian.tar.xz differ diff --git a/deb_dist/multibootusb_8.5.0-1.dsc b/deb_dist/multibootusb_8.5.0-1.dsc new file mode 100644 index 0000000..19faecf --- /dev/null +++ b/deb_dist/multibootusb_8.5.0-1.dsc @@ -0,0 +1,19 @@ +Format: 3.0 (quilt) +Source: multibootusb +Binary: python3-multibootusb +Architecture: all +Version: 8.5.0-1 +Maintainer: Sundar +Standards-Version: 3.9.1 +Build-Depends: python3-all, debhelper (>= 9) +Package-List: + python3-multibootusb deb system optional arch=all +Checksums-Sha1: + e1295668fecdb279e3cdbc53078f08cadf2a4948 4544191 multibootusb_8.5.0.orig.tar.gz + 0a0c598babb9369395d013ffafe88ffaf2b812bb 856 multibootusb_8.5.0-1.debian.tar.xz +Checksums-Sha256: + 7dd7a86b1a980a044045e224aa3145ef6177089a5e7601b6ac12175c86a3d1fe 4544191 multibootusb_8.5.0.orig.tar.gz + 38df7e3104f4ea0f14318854131e05ef0693bcfc4295d8fda4b0e5cdaaad1868 856 multibootusb_8.5.0-1.debian.tar.xz +Files: + 1eeb4bec75be17aaa260bc55088ab8ca 4544191 multibootusb_8.5.0.orig.tar.gz + a291c997070dd3133d0e560d7e27acec 856 multibootusb_8.5.0-1.debian.tar.xz diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/PKG-INFO b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/PKG-INFO new file mode 100644 index 0000000..de37e36 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: multibootusb +Version: 8.5.0 +Summary: Create multi boot live Linux on a USB disk... +Home-page: http://multibootusb.org/ +Author: Sundar +Author-email: feedback.multibootusb@gmail.com +License: General Public License (GPL) +Description: multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives. +Platform: Linux diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb.desktop b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb.desktop new file mode 100644 index 0000000..e17d8b8 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=multibootusb +Comment=Install multiple Linux Operating System on USB +Icon=multibootusb +Exec=multibootusb-pkexec +Categories=System; +StartupNotify=true diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/bg.png b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/bg.png new file mode 100644 index 0000000..629ed91 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/bg.png differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/chain.c32 b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/chain.c32 new file mode 100644 index 0000000..52d4c52 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/chain.c32 differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/extlinux.cfg b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/extlinux.cfg new file mode 100644 index 0000000..e69de29 diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/grub.exe b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/grub.exe new file mode 100644 index 0000000..57b890d Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/grub.exe differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/memdisk b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/memdisk new file mode 100644 index 0000000..411c07d Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/memdisk differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.c32 b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.c32 new file mode 100644 index 0000000..90318f0 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.c32 differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.lst b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.lst new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/menu.lst @@ -0,0 +1 @@ + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/syslinux.cfg b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/syslinux.cfg new file mode 100644 index 0000000..445bd5f --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/syslinux.cfg @@ -0,0 +1,35 @@ +# This file is created by MultiBootUSB. +default vesamenu.c32 +prompt 0 +menu title MultiBootUSB +MENU BACKGROUND /multibootusb/bg.png +TIMEOUT 300 +MENU WIDTH 80 +MENU MARGIN 10 +MENU PASSWORDMARGIN 3 +MENU ROWS 12 +MENU TABMSGROW 18 +MENU CMDLINEROW 18 +MENU ENDROW -1 +MENU PASSWORDROW 11 +MENU TIMEOUTROW 20 +MENU HELPMSGROW 22 +MENU HELPMSGENDROW -1 +MENU HIDDENROW -2 +MENU HSHIFT 0 +MENU VSHIFT 0 +MENU COLOR border 30;44 #40ffffff #a0000000 std +MENU COLOR title 1;36;44 #9033ccff #a0000000 std +MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all +MENU COLOR unsel 37;44 #50ffffff #a0000000 std +MENU COLOR help 37;40 #c0ffffff #a0000000 std +MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std +MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std +MENU COLOR msg07 37;40 #90ffffff #a0000000 std +MENU COLOR tabmsg 31;40 #30ffffff #00000000 std +label Boot from Hard Drive +MENU LABEL Boot from Hard Disk +KERNEL chain.c32 +APPEND hd1 +MENU DEFAULT + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 new file mode 100644 index 0000000..a92f4fe Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/multibootusb/vesamenu.c32 differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/dd.exe b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/dd.exe new file mode 100644 index 0000000..8cf719c Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/dd.exe differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/diskio.dll b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/diskio.dll new file mode 100644 index 0000000..1c6a7f0 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/dd/diskio.dll differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mbr.bin b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mbr.bin new file mode 100644 index 0000000..646a684 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mbr.bin differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe new file mode 100644 index 0000000..79690fd Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/mkfs/mke2fs.exe differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/multibootusb.png b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/multibootusb.png new file mode 100644 index 0000000..92c2e82 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/multibootusb.png differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip new file mode 100644 index 0000000..f16165d Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux.zip differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip new file mode 100644 index 0000000..7f84c74 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_linux_64.zip differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip new file mode 100644 index 0000000..b2a4fa5 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_modules.zip differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip new file mode 100644 index 0000000..3cb5897 Binary files /dev/null and b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/tools/syslinux/syslinux_windows.zip differ diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/version.txt b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/version.txt new file mode 100644 index 0000000..6d28907 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/data/version.txt @@ -0,0 +1 @@ +8.5.0 diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb new file mode 100644 index 0000000..974726e --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: multibootusb +# Purpose: Main file which will determine if cli or gui is to be opened +# 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 logging +import getopt +import sys +import os +import platform + +# Had trouble in importing scripts directory. Had to add few lines below to ensure it works on source as well as +# post install +try: + from scripts.mbusb_cli import * + from scripts import admin + from scripts import gen +except: + try: + from .scripts.mbusb_cli import * + from .scripts import admin + from .scripts import gen + except: + import scripts + +gui = True +iso_link = None +usb_disk = None +uninstall = False + +def usage(): + print('\nAn advanced multi boot live usb creator using command line.') + print('\nUsage: python3 multibootusb -c -i /path/to/iso [option(s)] -t /path/to/device\n') + print('[option(s)] are :\n') + print(' -h or --help : Print this help message and exit') + print(' -i or --iso : Path to ISO file') + print(' -t or --target : Path to target USB device partition (example /dev/sdb1)\n') + print(' -c or --command : This option is must for invoking multibootusb from command line\n') + print(' -u or --uninstall : List and uninstall distro from USB disk') + print(' Command line example for making a bootable USB from command line should look like this:-\n') + print(' python3 multibootusb -c -i ../../favourite.iso -t /dev/sdb1\n') + exit(2) + + +def start_gui(): + from scripts import mbusb_gui + gen.log('Starting multibootusb GUI...') + if platform.system() == 'Linux': + if os.getuid() != 0: + gen.log('\n\nAdmin privilege is required to run multibootusb.\n If you are running from source try ' + '\'sudo python3 ./multibootusb\'\n or you can try \'multibootusb-pkexec\' (post install)\n\n', + info=False, debug=True) + mbusb_gui.main_gui() + + +if __name__ == '__main__': + if platform.system() == 'Windows': + if not admin.isUserAdmin(): + admin.runAsAdmin() + sys.exit(0) + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:t:vhcu', + ['iso=', 'target=', 'version', 'help', 'command', 'uninstall']) + except getopt.GetoptError: + usage() + sys.exit(2) + for opt, arg in opts: + if opt in ('-h', '--help'): + usage() + sys.exit(2) + elif opt in ('-v', '--version'): + print_version() + sys.exit() + elif opt in ('-i', '--iso'): + config.iso_link = arg + elif opt in ('-t', '--target'): + config.usb_disk = arg + elif opt in ('-c', '--command'): + gui = False + elif opt in ('-u', '--uninstall'): + uninstall = True + else: + gui = True + #start_gui() + ''' + usage() + sys.exit() + ''' + +if gui is False: + if uninstall is True and config.usb_disk is not None: + cli_uninstall_distro() + elif config.iso_link is None and config.usb_disk is None: + usage() + elif config.iso_link is None or config.usb_disk is None: + print('\nOptions \'-i\' and \'t\' must be supplied together. See the usage below.') + usage() + else: + cli_install_distro() + +elif gui is True: + start_gui() diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb-pkexec b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb-pkexec new file mode 100644 index 0000000..f9f7e98 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/multibootusb-pkexec @@ -0,0 +1,6 @@ +#!/bin/bash +if [ $(which pkexec) ]; then + pkexec --disable-internal-agent "/usr/bin/multibootusb" "$@" +else + /usr/bin/multibootusb "$@" +fi \ No newline at end of file diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy new file mode 100644 index 0000000..e111748 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/org.debian.pkexec.run-multibootusb.policy @@ -0,0 +1,19 @@ + + + + + + Run the multibootusb program + Authentication is required to run the multibootusb + + auth_admin + auth_admin + auth_admin + + /usr/bin/multibootusb + true + + + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/_7zip.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/_7zip.py new file mode 100644 index 0000000..43f646a --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/_7zip.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: 7zip.py +# Purpose: Wrapper module to list and extract ISO files using 7zip +# 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 platform +import subprocess +from . import config +from . import gen + +if platform.system() == 'Windows': + _7zip = gen.quote(gen.resource_path(os.path.join('data', 'tools', '7zip', '7z.exe'))) +else: + _7zip = '7z' + + +def extract_iso(src, dst, pattern=None, suppress_out=True): + """ + Simple wrapper function to extract ISO file to destination + :param src: Path to ISO file + :param dst: Path to directory where the files are to be extracted + :param patter: The pattern to match the files to be extracted + :return: + """ + # 7z x -y -oC:\path_to_directory X:\path_to_iso_file.iso + # 7z e archive.zip -oC:\path_to_directory *.cfg *.bin -r + if platform.system() == 'Windows': + cli_option = ' -bb1' # Linux does not accept this option (may be due to version diff). + if suppress_out != '': + # suppress_out = ' 2> nul' + suppress_out = '' + else: + cli_option = '' + if suppress_out != '': + suppress_out = ' 2> /dev/null' + + if not os.path.exists(src): + gen.log('ISO file could not be found on the location specified.') + return False + if not os.path.exists(dst): + os.makedirs(dst, exist_ok=True) + + if pattern is None: + _cmd = _7zip + cli_option + ' x -y -o' + gen.quote(dst) + ' ' + gen.quote(src) + suppress_out + else: + _cmd = _7zip + ' -y x ' + gen.quote(src) + ' -o' + dst + ' ' + gen.quote(pattern) + ' -r' + suppress_out + # gen.log('Executing', _cmd) + _7zip_process = subprocess.Popen(_cmd, universal_newlines=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + config.status_text = 'Extracting ' + os.path.basename(src) + while True: + line = _7zip_process.stdout.readline() + # gen.log(line) + if line.startswith('- '): + config.status_text = 'Extracting ' + line[2:] + elif platform.system() == 'Linux': # line.startswith('Extracting'): # Under Linux it prints directly all the process (may be due to version diff). + config.status_text = line + if line == '' and _7zip_process.poll() != None: + break + + +def list_iso(iso_link, suppress_out=True): + if platform.system() == 'Windows': + if suppress_out is True: + suppress_out = ' 2> nul' + else: + if suppress_out is True: + suppress_out = ' 2> /dev/null' + if not os.path.exists(iso_link): + gen.log('Path to ISO link does not exist.') + return False + else: + file_list = [] + _cmd = _7zip + ' l ' + gen.quote(iso_link) + suppress_out + # gen.log('Executing', _cmd) + _cmd_out = subprocess.check_output(_cmd, universal_newlines=True, stderr=subprocess.PIPE, shell=True).splitlines() + for line in _cmd_out: + line = line.split() + if '.....' in line: + file_list.append(line[-1]) + + return file_list + + +def test_iso(iso_link, suppress_out=True): + """ + Function to test if ISO file is corrupted. Relying only on 7zip. + :param iso_link: Path to ISO file + :return: True if test is positive + """ + # 7z t /path/to/iso/file.iso + # return value : 0 No error + # return value : 1 Warning (Non fatal error(s)) + # return value : 2 Fatal error + # return value : 7 Command line error + # return value : 8 Not enough memory for operation + # return value : 255 User stopped the process + + if platform.system() == 'Windows': + if suppress_out is True: + suppress_out = ' > nul' + else: + if suppress_out is True: + suppress_out = ' > /dev/null' + + _cmd = _7zip + ' t ' + iso_link + suppress_out + + # gen.log('Executing', _cmd) + + rc = subprocess.call(_cmd, shell=True) + + if rc == 0 or rc == 1: + return True + else: + return False + + +if __name__ == '__main__': + # slitaz-4.0.iso + # ubuntu-16.04-desktop-amd64.iso + # avg_arl_cdi_all_120_160420a12074.iso + # haiku-nightly.iso + # Hiren_BootCD.iso + file_list = list_iso('../../ubuntu_14_04_backup/Downloads/clonezilla-live-2.4.2-32-amd64.iso') + for f in file_list: + print(f) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/__init__.py new file mode 100644 index 0000000..c5c69ae --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/__init__.py @@ -0,0 +1,10 @@ +# Author(s) information +__authors__ = ['Sundar'] +__author__ = ','.join(__authors__) +__credits__ = ['Ian Bruce', 'Lee'] +__copyright__ = 'Copyright (c) 2014' +__license__ = 'GPL' + +# Maintanence information +__maintainer__ = 'Sundar' +__email__ = 'feedback.multibootusb@gmail.com' diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/admin.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/admin.py new file mode 100644 index 0000000..9e280f1 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/admin.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4 +# Name: admin.py +# Purpose: Module to ask for admin rights under Linux and Windows +# Authors: Originally developed by Preston Landers (for windows) and modified for multibootusb by Sundar +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of the same license as Python 2.6.5 + +## +## (C) COPYRIGHT © Preston Landers 2010 +## Released under the same license as Python 2.6.5 +## + +""" +User Access Control for Microsoft Windows Vista and higher. This is +only for the Windows platform. + +This will relaunch either the current script - with all the same command +line parameters - or else you can provide a different script/program to +run. If the current user doesn't normally have admin rights, he'll be +prompted for an admin password. Otherwise he just gets the UAC prompt. + +This is meant to be used something like this:: + + if not pyuac.isUserAdmin(): + return pyuac.runAsAdmin() + + # otherwise carry on doing whatever... + +See L{runAsAdmin} for the main interface. + +""" +import os +import traceback +import types +import platform +import sys +import subprocess +from PyQt5 import QtWidgets +from . import gen + + +def isUserAdmin(): + """ + @return: True if the current user is an 'Admin' whatever that means + (root on Unix), otherwise False. + + Warning: The inner function fails unless you have Windows XP SP2 or + higher. The failure causes a traceback to be gen.loged and this + function to return False. + """ + + if platform.system() == "Windows": + import ctypes + # WARNING: requires Windows XP SP2 or higher! + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + traceback.print_exc() + gen.log("Admin check failed, assuming not an admin.") + return False + elif platform.system() == "Linux": + return os.getuid() == 0 + else: + raise RuntimeError("Unsupported operating system for this module: %s" % (os.name,)) + + +def runAsAdmin(cmdLine=None, wait=True): + """ + Attempt to relaunch the current script as an admin using the same + command line parameters. Pass cmdLine in to override and set a new + command. It must be a list of [command, arg1, arg2...] format. + + Set wait to False to avoid waiting for the sub-process to finish. You + will not be able to fetch the exit code of the process if wait is + False. + + Returns the sub-process return code, unless wait is False in which + case it returns None. + + @WARNING: this function only works on Windows. + """ + + #if os.name == 'nt': + # raise RuntimeError, "This function is only implemented on Windows." + if platform.system() == "Windows": + + import win32api, win32con, win32event, win32process + from win32com.shell.shell import ShellExecuteEx + from win32com.shell import shellcon + + python_exe = sys.executable + + if cmdLine is None: + cmdLine = [python_exe] + sys.argv + elif type(cmdLine) not in (types.TupleType, types.ListType): + raise ValueError("cmdLine is not a sequence.") + cmd = '"%s"' % (cmdLine[0],) + # XXX TODO: isn't there a function or something we can call to massage command line params? + params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]]) + cmdDir = '' + showCmd = win32con.SW_SHOWNORMAL + #showCmd = win32con.SW_HIDE + lpVerb = 'runas' # causes UAC elevation prompt. + + #gen.log("Running", cmd, params) + + # ShellExecute() doesn't seem to allow us to fetch the PID or handle + # of the process, so we can't get anything useful from it. Therefore + # the more complex ShellExecuteEx() must be used. + + # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd) + + procInfo = ShellExecuteEx(nShow=showCmd, + fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, + lpVerb=lpVerb, + lpFile=cmd, + lpParameters=params) + + if wait: + procHandle = procInfo['hProcess'] + obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE) + rc = win32process.GetExitCodeProcess(procHandle) + #gen.log "Process handle %s returned code %s" % (procHandle, rc) + else: + rc = None + + return rc + + +def adminCmd(cmd, fork=False, gui=False): + """ + This simple function checks for a sudo command and runs a command using it. + This function tries to launch given script with root access using pkexec/gksu/gksudo/kdesu/kdesudo, + if one of them is already installed. + PyQt4 is used as GUI. + Author : sundar + """ + sudo_cmd = '' + if os.getuid() == 0: + sudo_cmd = cmd + else: + if os.system('which pkexec') == 0: + if gui: + cmd = ['export DISPLAY=$DISPLAY; export XAUTHORITY=$XAUTHORITY; '] + cmd # By default, pkexec disallows X11 apps. Restore DISPLAY & XAUTHORITY to allow it. man 1 pkexec/"SECURITY NOTES" section + sudo_cmd = ['pkexec', '/bin/sh', '-c'] + elif os.system('which gksudo') == 0: + sudo_cmd = ["gksudo", "--", "/bin/sh", "-c"] + elif os.system('which gksu') == 0: + sudo_cmd = ["gksu"] + elif os.system('which kdesudo') == 0: + sudo_cmd = ["kdesudo", "-t", "-c"] # http://www.unix.com/man-page/debian/1/kdesudo/ + elif os.system('which kdesu') == 0: + sudo_cmd = ["kdesu", "-t", "-c"] # http://linux.die.net/man/1/kdesu + else: + QtWidgets.QMessageBox.information('No root...', + 'Could not find any of: pkexec, sudo, gksu, kdesu, gksudo, or kdesudo. Please install one then restart multibootusb.') + sys.exit(0) + final_cmd = ' '.join(sudo_cmd + ['"' + ' '.join(cmd).replace('"', '\\"') + '"']) + gen.log("Executing ==> " + final_cmd) + if fork: + return subprocess.Popen(final_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, shell=True) + else: + ret = subprocess.call(final_cmd, shell=True) + gen.log("Process returned ==> " + str(ret)) + return ret diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/config.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/config.py new file mode 100644 index 0000000..e80a187 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/config.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# Name: config.py +# Purpose: Module to share important variables between various modules. Mainly included so as not to call many +# functions again and again +# 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 + +iso_link = "" +usb_disk = "" +usb_mount = "" +usb_uuid = "" +usb_label = "" +persistence = 0 +distro = "" +status_text = "" +percentage = 0 +syslinux_version = '' +uninstall_distro_dir_name = "" +uninstall_distro_dir_path = "" +iso_file_list = '' + +process_exist = None + +imager_iso_link = "" +imager_usb_disk_selected = "" +imager_lock = "" +imager_percentage = "" +imager_status_text = "" + +install_size = "" + +editors_linux = ["gedit", "kate", "kwrite"] +editors_win = ["notepad++.exe", "notepad.exe"] + +imager_usb_disk = [] diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/distro.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/distro.py new file mode 100644 index 0000000..0c788ec --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/distro.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: distro.py +# Purpose: Module to detect if distro types supported by multibootusb (by extracting specific files) +# 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 string +import platform +import re +from .iso import * +from .isodump3 import ISO9660 +from .gen import * +from . import _7zip +from . import config + + +def distro(iso_cfg_ext_dir, iso_link): + """ + Detect if distro is supported by multibootusb. + :param iso_cfg_ext_dir: Directory where *.cfg files are extracted. + :return: Detected distro name as string. + """ + iso9660fs = ISO9660(iso_link) + # iso_file_list = iso9660fs.readDir("/") + iso_file_list = _7zip.list_iso(iso_link) + if platform.system() == "Linux" or platform.system() == "Windows": + for path, subdirs, files in os.walk(iso_cfg_ext_dir): + for name in files: + if name.endswith('.cfg') or name.endswith('.CFG') or name.endswith('.txt') or name.endswith('.TXT') \ + or name.endswith('.lst'): + try: + # errors='ignore' is required as some files also contain non utf character + string = open(os.path.join(path, name), errors='ignore').read() + except IOError: + return "Read Error." + else: + if any("f4ubcd" in s.lower() for s in iso_file_list): + return "f4ubcd" + if re.search(r'ubcd', string, re.I): + return "ubcd" + elif re.search(r'Super Grub Disk', string, re.I): + return "sgrubd2" + elif re.search(r'hbcd', string, re.I): + return "hbcd" + elif re.search(r'systemrescuecd', string, re.I): + return "systemrescuecd" + elif re.search(r'pmagic|partedmagic', string, re.I) and isolinux_bin_exist(iso_link): + return "parted-magic" + elif re.search(r'mgalive', string, re.I): # mounting fat filesystem hard coded in to initrd. + # Can be modified only under linux. + return "mageialive" + elif re.search(r'archisolabel|misolabel', string, re.I): + return "arch" + elif re.search(r'chakraisolabel', string, re.I): + return "chakra" + elif re.search(r'kdeosisolabel', string, re.I): + return "kaos" + elif re.search(r'boot=live', string, re.I) and isolinux_bin_exist(iso_link): + return "debian" + elif re.search(r'debian-installer', string, re.I) and isolinux_bin_exist(iso_link): + return "debian-install" + elif re.search(r'solydx', string, re.I): + return "solydx" + elif re.search(r'knoppix', string, re.I): + return "knoppix" + elif re.search(r'root=live:CDLABEL=', string, re.I): + return "fedora" + elif re.search(r'redhat', string, re.I): + return "redhat" + # elif re.search(r'suse', string, re.I): + # return "suse" + elif re.search(r'opensuse', string, re.I): + return "opensuse" + elif re.search( + r'slitaz|dban |ophcrack|tinycore|rescue.cpi|xpud|untangle|4mlinux|partition wizard|android-x86.png|' + r'riplinux|lebel dummy|http://pogostick.net/~pnh/ntpasswd/|AVG Rescue CD', string, re.I): + return "slitaz" + elif re.search(r'boot=casper', string, re.I): + return "ubuntu" + elif re.search(r'wifislax', string, re.I): + return "wifislax" + elif re.search(r'slax', string, re.I): + return "slax" + elif re.search(r'sms|vector|autoexec', string, re.I) and isolinux_bin_exist(iso_link): + return "sms" + elif re.search(r'antix', string, re.I): + return "antix" + elif re.search(r'porteus', string, re.I): + return "porteus" + elif re.search(r'livecd=livecd|PCLinuxOS', string, re.I): + return "pclinuxos" + elif re.search(r'looptype=squashfs', string, re.I): + return "gentoo" + elif re.search(r'finnix', string, re.I): + return "finnix" + elif re.search(r'wifiway', string, re.I): + return "wifiway" + elif re.search(r'puppy|quirky', string, re.I): + return "puppy" + elif re.search(r'ipcop', string, re.I): + return "ipcop" + elif re.search(r'ipfire', string, re.I): + return "ipfire" + elif re.search(r'zenwalk|slack|salix', string, re.I) and re.search(r'live', string, re.I): + return "salix-live" + elif re.search(r'zenwalk|slack|salix', string, re.I): + return "zenwalk" + elif re.search(r'ubuntu server', string, re.I): + return "ubuntu-server" + elif re.search(r'Welcome to CentOS', string, re.I): + return "centos-net-minimal" + elif re.search(r'Trinity Rescue Kit', string, re.I): + return "trinity-rescue" + elif re.search(r'alpine', string, re.I): + return "alpine" + elif re.search(r'http://support.kaspersky.com', string, re.I): + return "kaspersky" + elif re.search(r'ALT Linux', string, re.I): + return "alt-linux" + elif re.search(r'Sergei Strelec', string, re.I): + return "Windows" + elif re.search(r'ReactOS', string, re.I): + return "ReactOS" + elif re.search(r'fsecure', string, re.I): + return "fsecure" + elif re.search(r'default rwp', string, re.I): + return "pc-unlocker" + + + distro = detect_iso_from_file_list(iso_link) + if distro: + return distro + # FIXME: See the below comments. + ''' + else: + # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails. + # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory. + # All works fine but unable to boot the distro successfully. Also, see the generic section from + # syslinux, update_cfg and install_distro modules. + if self.isolinux_bin_exist(): + return "generic" + ''' + else: + return None + + +def detect_iso_from_file_list(iso_link): + """ + Fallback detection script from the content of an ISO. + :return: supported distro as string + """ + if os.path.exists(iso_link): + iso_file_list = _7zip.list_iso(iso_link) + if any("sources" in s.lower() for s in iso_file_list) and any("boot.wim" in s.lower() for s in iso_file_list): + return "Windows" + elif any("config.isoclient" in s.lower() for s in iso_file_list): + return "opensuse" + elif any("dban" in s.lower() for s in iso_file_list): + return "slitaz" + elif any("memtest.img" in s.lower() for s in iso_file_list): + return "mentest" + elif any("menu.lst" in s.lower() for s in iso_file_list): + return "grub4dos" + elif any("bootwiz.cfg" in s.lower() for s in iso_file_list) and any("bootmenu_logo.png" in s.lower() for s in iso_file_list): + return "grub4dos_iso" + else: + log(iso_file_list) + +if __name__ == '__main__': + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + iso_link = 'Downloads/clonezilla-live-2.4.2-32-amd64.iso' + iso_extract_file(iso_link, iso_cfg_ext_dir, 'cfg') + log(distro(iso_cfg_ext_dir)) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gen.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gen.py new file mode 100644 index 0000000..461821c --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gen.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: log.py +# Purpose: This 'general' module contain many functions required to be called at many places +# 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 logging +import sys +import os +import platform +import shutil +import string +import zipfile +import tempfile + + +def scripts_dir_path(): + return os.path.dirname(os.path.realpath(__file__)) + + +def log(message, info=True, error=False, debug=False): + """ + 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)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.DEBUG) + print(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__))) + + if os.path.exists(os.path.join(basePath, relativePath)): + path = os.path.join(basePath, relativePath) + return path + elif not os.path.exists(os.path.join(basePath, relativePath)): + if os.path.exists(os.path.join(os.path.abspath("."), relativePath)): + basePath = os.path.abspath(".") + elif os.path.exists(os.path.join(os.path.abspath(".."), relativePath)): + basePath = os.path.abspath("..") + path = os.path.join(basePath, relativePath) + return path + + +def print_version(): + """ + Simple log the version number of the multibootusb application + :return: + """ + log('multibootusb version : ', mbusb_version()) + + +def quote(text): + """ + Function to quote the input word or sentence. + :param text: Any word or sentence. + :return: Quoted text or sentence. If already quoted the same text is returned. + """ + if not is_quoted(text): + return '"' + text + '"' + else: + return text + + +def is_quoted(text): + """ + Function to check if word is quoted. + :param text: Any word or sentence with or without quote. + :return: True if text is quoted else False. + """ + if text.startswith("""") and text.endswith("""): + return True + else: + return False + + +def has_digit(word): + """ + Useful function to detect if input word contain digit. + :param word: Any alphanumeric word. + :return: True if the word has a digit else False. + """ + return any(char.isdigit() for char in word) + + +def sys_64bits(): + """ + Detect if the host system is 64 bit. + :return: True if system is 64 bit. + """ + 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 + """ + 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. + :return: Path to multibootusb directory of host system. + """ + if platform.system() == "Linux": + home_dir = os.path.expanduser('~') + mbusb_dir = os.path.join(home_dir, ".multibootusb") + elif platform.system() == "Windows": + mbusb_dir = os.path.join(tempfile.gettempdir(), "multibootusb") + + return mbusb_dir + + +def iso_cfg_ext_dir(): + """ + Function to return the path to ISO configuration file extraction directory. + :return: Path to directory where ISO config files will be extracted. + """ + return os.path.join(multibootusb_host_dir(), 'iso_cfg_ext_dir') + + +def clean_iso_cfg_ext_dir(iso_cfg_ext_dir): + """ + Clean old ISO config files extracted by previous use of multibootusb. + :param iso_cfg_ext_dir: Path to config extract directory. + :return: + """ + if os.path.exists(iso_cfg_ext_dir): + filelist = [f for f in os.listdir(iso_cfg_ext_dir)] + for f in filelist: + if os.path.isdir(os.path.join(iso_cfg_ext_dir, f)): + shutil.rmtree(os.path.join(iso_cfg_ext_dir, f)) + else: + os.remove(os.path.join(iso_cfg_ext_dir, f)) + else: + log('iso_cfg_ext_dir directory does not exist.') + + +def copy_mbusb_dir_usb(usb_disk): + """ + Copy the multibootusb directory to USB mount path. + :param usb_mount_path: Path to USB mount. + :return: + """ + from .iso import iso_size + from .usb import details + + usb_details = details(usb_disk) + usb_mount_path = usb_details['mount_point'] + if not os.path.exists(os.path.join(usb_mount_path, "multibootusb")): + try: + log('Copying multibootusb directory to ' + usb_mount_path) + shutil.copytree(resource_path(os.path.join("data", "multibootusb")), os.path.join(usb_mount_path, "multibootusb")) + return True + except: + return False + else: + log('multibootus directory already exist. Not copying.') + + +def read_input_yes(): + """ + List option and read user input + :return: True if user selected yes or else false + """ + yes_list = ['Y', 'y', 'Yes', 'yes', 'YES'] + no_list = ['N', 'n', 'No', 'no', 'NO'] + response = input("Please enter the option listed above : ") + if response in yes_list: + return True + elif response in no_list: + return False + + +def strings(filename, min=4): + with open(filename, errors="ignore") as f: + result = "" + for c in f.read(): + if c in string.printable: + result += c + continue + if len(result) >= min: + yield result + result = "" + if len(result) >= min: # catch result at EOF + yield result + + +def size_not_enough(iso_link, usb_disk): + from .iso import iso_size + from .usb import details + isoSize = iso_size(iso_link) + usb_details = details(usb_disk) + usb_size = usb_details['size_free'] + if isoSize > usb_size: + return True + else: + return False + + +def mbusb_version(): + version = open(resource_path(os.path.join("data", "version.txt")), 'r').read().strip() + return version + + +def prepare_mbusb_host_dir(): + """ + Prepare multibootusb host directory and extract data files for use. + :return: + """ + home = multibootusb_host_dir() + if not os.path.exists(home): + os.makedirs(home) + else: + log("Cleaning old multibootusb directory...") + shutil.rmtree(home) + os.makedirs(home) + + if not os.path.exists(os.path.join(home, "preference")): + os.makedirs(os.path.join(home, "preference")) + + if not os.path.exists(os.path.join(home, "iso_cfg_ext_dir")): + os.makedirs(os.path.join(home, "iso_cfg_ext_dir")) + + if os.path.exists(os.path.join(home, "syslinux", "bin", "syslinux4")): + log("Syslinux exist in multibootusb directory...") + else: + log("Extracting syslinux to multibootusb directory...") + if platform.system() == "Linux": + if sys_64bits() is True: + log('Host OS is 64 bit...') + log("Extracting syslinux 64 bit...") + # log(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip"))) + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux_64.zip")), "r") as z: + z.extractall(home) + else: + log("Extracting syslinux 32 bit...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_linux.zip")), "r") as z: + z.extractall(home) + else: + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_windows.zip")), "r") as z: + z.extractall(home) + log("Extracting syslinux modules to multibootusb directory...") + with zipfile.ZipFile(resource_path(os.path.join("data", "tools", "syslinux", "syslinux_modules.zip")), "r") as z: + z.extractall(os.path.join(home, "syslinux")) + + if os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + log(os.listdir(os.path.join(home, "iso_cfg_ext_dir"))) + log("iso extract directory is not empty.") + log("Removing junk files...") + for files in os.listdir(os.path.join(home, "iso_cfg_ext_dir")): + if os.path.isdir(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))): + log (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + shutil.rmtree(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + else: + try: + log (os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.chmod(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files)), 0o777) + os.unlink(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + os.remove(os.path.join(os.path.join(home, "iso_cfg_ext_dir", files))) + except OSError: + log("Can't remove the file. Skipping it.") + +if __name__ == '__main__': + log(quote("""Test-string""")) + log(has_digit("test-string-with-01-digit")) + log(sys_64bits()) + log(multibootusb_host_dir()) + log(iso_cfg_ext_dir()) + strings_test = strings('../../text-stings.bin') + log(strings_test) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gui/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py new file mode 100644 index 0000000..d5c8913 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/gui/ui_multibootusb.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'scripts\gui\multibootusb.ui' +# +# Created by: PyQt5 UI code generator 5.6 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(644, 516) + self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog) + self.horizontalLayout.setObjectName("horizontalLayout") + self.tabWidget = QtWidgets.QTabWidget(Dialog) + self.tabWidget.setObjectName("tabWidget") + self.tab_3 = QtWidgets.QWidget() + self.tab_3.setObjectName("tab_3") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab_3) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_persistence_value = QtWidgets.QLabel(self.tab_3) + self.label_persistence_value.setObjectName("label_persistence_value") + self.gridLayout.addWidget(self.label_persistence_value, 5, 2, 1, 1) + self.verticalLayout_11 = QtWidgets.QVBoxLayout() + self.verticalLayout_11.setObjectName("verticalLayout_11") + self.groupBox_11 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_11.setObjectName("groupBox_11") + self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.groupBox_11) + self.horizontalLayout_8.setObjectName("horizontalLayout_8") + self.detect_usb = QtWidgets.QPushButton(self.groupBox_11) + self.detect_usb.setObjectName("detect_usb") + self.horizontalLayout_8.addWidget(self.detect_usb) + self.verticalLayout_11.addWidget(self.groupBox_11) + self.gridLayout.addLayout(self.verticalLayout_11, 1, 3, 1, 2) + self.listWidget = QtWidgets.QListWidget(self.tab_3) + self.listWidget.setObjectName("listWidget") + self.gridLayout.addWidget(self.listWidget, 0, 0, 4, 3) + self.progressBar = QtWidgets.QProgressBar(self.tab_3) + self.progressBar.setProperty("value", 0) + self.progressBar.setObjectName("progressBar") + self.gridLayout.addWidget(self.progressBar, 7, 0, 1, 6) + self.create = QtWidgets.QPushButton(self.tab_3) + self.create.setObjectName("create") + self.gridLayout.addWidget(self.create, 5, 4, 1, 1) + self.comboBox = QtWidgets.QComboBox(self.tab_3) + self.comboBox.setObjectName("comboBox") + self.gridLayout.addWidget(self.comboBox, 0, 3, 1, 1) + self.labelstep1 = QtWidgets.QLabel(self.tab_3) + self.labelstep1.setObjectName("labelstep1") + self.gridLayout.addWidget(self.labelstep1, 0, 5, 1, 1) + self.labelstep2 = QtWidgets.QLabel(self.tab_3) + self.labelstep2.setObjectName("labelstep2") + self.gridLayout.addWidget(self.labelstep2, 4, 5, 1, 1) + self.close = QtWidgets.QPushButton(self.tab_3) + self.close.setObjectName("close") + self.gridLayout.addWidget(self.close, 5, 3, 1, 1) + self.labelstep3 = QtWidgets.QLabel(self.tab_3) + self.labelstep3.setObjectName("labelstep3") + self.gridLayout.addWidget(self.labelstep3, 5, 5, 1, 1) + self.slider_persistence = QtWidgets.QSlider(self.tab_3) + self.slider_persistence.setEnabled(False) + self.slider_persistence.setAutoFillBackground(False) + self.slider_persistence.setOrientation(QtCore.Qt.Horizontal) + self.slider_persistence.setTickPosition(QtWidgets.QSlider.TicksBothSides) + self.slider_persistence.setTickInterval(0) + self.slider_persistence.setObjectName("slider_persistence") + self.gridLayout.addWidget(self.slider_persistence, 5, 1, 1, 1) + self.groupBox = QtWidgets.QGroupBox(self.tab_3) + self.groupBox.setObjectName("groupBox") + self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout_5 = QtWidgets.QGridLayout() + self.gridLayout_5.setObjectName("gridLayout_5") + self.uninstall = QtWidgets.QPushButton(self.groupBox) + self.uninstall.setObjectName("uninstall") + self.gridLayout_5.addWidget(self.uninstall, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_5) + self.gridLayout.addWidget(self.groupBox, 3, 3, 1, 2) + self.lineEdit = QtWidgets.QLineEdit(self.tab_3) + self.lineEdit.setObjectName("lineEdit") + self.gridLayout.addWidget(self.lineEdit, 4, 0, 1, 4) + self.groupBox_6 = QtWidgets.QGroupBox(self.tab_3) + self.groupBox_6.setObjectName("groupBox_6") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_6) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.usb_dev = QtWidgets.QLabel(self.groupBox_6) + self.usb_dev.setObjectName("usb_dev") + self.verticalLayout_5.addWidget(self.usb_dev) + self.usb_vendor = QtWidgets.QLabel(self.groupBox_6) + self.usb_vendor.setObjectName("usb_vendor") + self.verticalLayout_5.addWidget(self.usb_vendor) + self.usb_model = QtWidgets.QLabel(self.groupBox_6) + self.usb_model.setObjectName("usb_model") + self.verticalLayout_5.addWidget(self.usb_model) + self.usb_size = QtWidgets.QLabel(self.groupBox_6) + self.usb_size.setObjectName("usb_size") + self.verticalLayout_5.addWidget(self.usb_size) + self.usb_mount = QtWidgets.QLabel(self.groupBox_6) + self.usb_mount.setObjectName("usb_mount") + self.verticalLayout_5.addWidget(self.usb_mount) + self.gridLayout.addWidget(self.groupBox_6, 2, 3, 1, 3) + self.browse_iso = QtWidgets.QPushButton(self.tab_3) + self.browse_iso.setObjectName("browse_iso") + self.gridLayout.addWidget(self.browse_iso, 4, 4, 1, 1) + self.label_persistence = QtWidgets.QLabel(self.tab_3) + self.label_persistence.setObjectName("label_persistence") + self.gridLayout.addWidget(self.label_persistence, 5, 0, 1, 1) + self.checkBox_all_drives = QtWidgets.QCheckBox(self.tab_3) + self.checkBox_all_drives.setObjectName("checkBox_all_drives") + self.gridLayout.addWidget(self.checkBox_all_drives, 0, 4, 1, 1) + self.status = QtWidgets.QLabel(self.tab_3) + self.status.setMinimumSize(QtCore.QSize(0, 0)) + self.status.setAcceptDrops(False) + self.status.setAutoFillBackground(False) + self.status.setFrameShadow(QtWidgets.QFrame.Plain) + self.status.setText("") + self.status.setTextFormat(QtCore.Qt.AutoText) + self.status.setScaledContents(False) + self.status.setObjectName("status") + self.gridLayout.addWidget(self.status, 6, 0, 1, 6) + self.horizontalLayout_2.addLayout(self.gridLayout) + self.tabWidget.addTab(self.tab_3, "") + self.imager = QtWidgets.QWidget() + self.imager.setObjectName("imager") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.imager) + self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.gridLayout_9 = QtWidgets.QGridLayout() + self.gridLayout_9.setObjectName("gridLayout_9") + self.groupBox_7 = QtWidgets.QGroupBox(self.imager) + self.groupBox_7.setObjectName("groupBox_7") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_7) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.gridLayout_11 = QtWidgets.QGridLayout() + self.gridLayout_11.setObjectName("gridLayout_11") + self.groupBox_9 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_9.setObjectName("groupBox_9") + self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.groupBox_9) + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.comboBox_2 = QtWidgets.QComboBox(self.groupBox_9) + self.comboBox_2.setObjectName("comboBox_2") + self.verticalLayout_8.addWidget(self.comboBox_2) + self.pushbtn_imager_refreshusb = QtWidgets.QPushButton(self.groupBox_9) + self.pushbtn_imager_refreshusb.setObjectName("pushbtn_imager_refreshusb") + self.verticalLayout_8.addWidget(self.pushbtn_imager_refreshusb) + self.imager_disk_label = QtWidgets.QLabel(self.groupBox_9) + self.imager_disk_label.setObjectName("imager_disk_label") + self.verticalLayout_8.addWidget(self.imager_disk_label) + self.imager_total_size = QtWidgets.QLabel(self.groupBox_9) + self.imager_total_size.setObjectName("imager_total_size") + self.verticalLayout_8.addWidget(self.imager_total_size) + self.imager_uuid = QtWidgets.QLabel(self.groupBox_9) + self.imager_uuid.setObjectName("imager_uuid") + self.verticalLayout_8.addWidget(self.imager_uuid) + self.gridLayout_11.addWidget(self.groupBox_9, 0, 0, 1, 1) + self.groupBox_10 = QtWidgets.QGroupBox(self.groupBox_7) + self.groupBox_10.setObjectName("groupBox_10") + self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.groupBox_10) + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.pushButton = QtWidgets.QPushButton(self.groupBox_10) + self.pushButton.setObjectName("pushButton") + self.verticalLayout_9.addWidget(self.pushButton) + self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_10) + self.lineEdit_3.setObjectName("lineEdit_3") + self.verticalLayout_9.addWidget(self.lineEdit_3) + self.imager_bootable = QtWidgets.QLabel(self.groupBox_10) + self.imager_bootable.setObjectName("imager_bootable") + self.verticalLayout_9.addWidget(self.imager_bootable) + self.imager_iso_size = QtWidgets.QLabel(self.groupBox_10) + self.imager_iso_size.setObjectName("imager_iso_size") + self.verticalLayout_9.addWidget(self.imager_iso_size) + self.gridLayout_11.addWidget(self.groupBox_10, 0, 1, 1, 1) + self.verticalLayout_6.addLayout(self.gridLayout_11) + self.gridLayout_9.addWidget(self.groupBox_7, 1, 0, 1, 1) + self.groupBox_8 = QtWidgets.QGroupBox(self.imager) + self.groupBox_8.setObjectName("groupBox_8") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_8) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.imager_label_status = QtWidgets.QLabel(self.groupBox_8) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.imager_label_status.sizePolicy().hasHeightForWidth()) + self.imager_label_status.setSizePolicy(sizePolicy) + self.imager_label_status.setText("") + self.imager_label_status.setObjectName("imager_label_status") + self.verticalLayout_7.addWidget(self.imager_label_status) + self.gridLayout_12 = QtWidgets.QGridLayout() + self.gridLayout_12.setObjectName("gridLayout_12") + self.imager_write = QtWidgets.QPushButton(self.groupBox_8) + self.imager_write.setObjectName("imager_write") + self.gridLayout_12.addWidget(self.imager_write, 2, 2, 1, 1) + self.imager_close = QtWidgets.QPushButton(self.groupBox_8) + self.imager_close.setObjectName("imager_close") + self.gridLayout_12.addWidget(self.imager_close, 2, 1, 1, 1) + self.label_10 = QtWidgets.QLabel(self.groupBox_8) + self.label_10.setObjectName("label_10") + self.gridLayout_12.addWidget(self.label_10, 1, 0, 1, 3) + self.imager_progressbar = QtWidgets.QProgressBar(self.groupBox_8) + self.imager_progressbar.setProperty("value", 0) + self.imager_progressbar.setObjectName("imager_progressbar") + self.gridLayout_12.addWidget(self.imager_progressbar, 0, 0, 1, 3) + self.verticalLayout_7.addLayout(self.gridLayout_12) + self.gridLayout_9.addWidget(self.groupBox_8, 2, 0, 1, 1) + self.horizontalLayout_7.addLayout(self.gridLayout_9) + self.tabWidget.addTab(self.imager, "") + self.syslinux_ab = QtWidgets.QWidget() + self.syslinux_ab.setObjectName("syslinux_ab") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.syslinux_ab) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.gridLayout_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.groupBox_2 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_2.setAutoFillBackground(False) + self.groupBox_2.setObjectName("groupBox_2") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_2) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.gridLayout_3 = QtWidgets.QGridLayout() + self.gridLayout_3.setObjectName("gridLayout_3") + self.install_syslinux = QtWidgets.QPushButton(self.groupBox_2) + self.install_syslinux.setObjectName("install_syslinux") + self.gridLayout_3.addWidget(self.install_syslinux, 2, 1, 1, 1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem, 2, 0, 1, 1) + self.install_sys_all = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_all.setObjectName("install_sys_all") + self.gridLayout_3.addWidget(self.install_sys_all, 1, 0, 1, 2) + self.install_sys_only = QtWidgets.QRadioButton(self.groupBox_2) + self.install_sys_only.setObjectName("install_sys_only") + self.gridLayout_3.addWidget(self.install_sys_only, 0, 0, 1, 2) + self.horizontalLayout_4.addLayout(self.gridLayout_3) + self.gridLayout_2.addWidget(self.groupBox_2, 0, 0, 1, 1) + self.groupBox_3 = QtWidgets.QGroupBox(self.syslinux_ab) + self.groupBox_3.setObjectName("groupBox_3") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_3) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.gridLayout_4 = QtWidgets.QGridLayout() + self.gridLayout_4.setObjectName("gridLayout_4") + self.edit_syslinux = QtWidgets.QPushButton(self.groupBox_3) + self.edit_syslinux.setObjectName("edit_syslinux") + self.gridLayout_4.addWidget(self.edit_syslinux, 1, 1, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_4.addItem(spacerItem1, 1, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(self.groupBox_3) + self.label_2.setObjectName("label_2") + self.gridLayout_4.addWidget(self.label_2, 0, 0, 1, 2) + self.horizontalLayout_5.addLayout(self.gridLayout_4) + self.gridLayout_2.addWidget(self.groupBox_3, 1, 0, 1, 1) + self.horizontalLayout_3.addLayout(self.gridLayout_2) + self.tabWidget.addTab(self.syslinux_ab, "") + self.tab = QtWidgets.QWidget() + self.tab.setObjectName("tab") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.groupBox_5 = QtWidgets.QGroupBox(self.tab) + self.groupBox_5.setObjectName("groupBox_5") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_5) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.gridLayout_7 = QtWidgets.QGridLayout() + self.gridLayout_7.setObjectName("gridLayout_7") + self.ram_iso_2048 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_2048.setObjectName("ram_iso_2048") + self.gridLayout_7.addWidget(self.ram_iso_2048, 4, 4, 1, 1) + self.ram_iso_1024 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_1024.setObjectName("ram_iso_1024") + self.gridLayout_7.addWidget(self.ram_iso_1024, 4, 3, 1, 1) + self.ram_iso_256 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_256.setObjectName("ram_iso_256") + self.gridLayout_7.addWidget(self.ram_iso_256, 4, 0, 1, 1) + self.browse_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.browse_iso_qemu.setObjectName("browse_iso_qemu") + self.gridLayout_7.addWidget(self.browse_iso_qemu, 2, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_5) + self.label_7.setObjectName("label_7") + self.gridLayout_7.addWidget(self.label_7, 0, 0, 1, 5) + self.ram_iso_512 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_512.setObjectName("ram_iso_512") + self.gridLayout_7.addWidget(self.ram_iso_512, 4, 1, 1, 1) + self.boot_iso_qemu = QtWidgets.QPushButton(self.groupBox_5) + self.boot_iso_qemu.setObjectName("boot_iso_qemu") + self.gridLayout_7.addWidget(self.boot_iso_qemu, 6, 4, 1, 1) + self.ram_iso_768 = QtWidgets.QRadioButton(self.groupBox_5) + self.ram_iso_768.setObjectName("ram_iso_768") + self.gridLayout_7.addWidget(self.ram_iso_768, 4, 2, 1, 1) + self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_2.setObjectName("lineEdit_2") + self.gridLayout_7.addWidget(self.lineEdit_2, 2, 0, 1, 4) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem2, 3, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.groupBox_5) + self.label_3.setObjectName("label_3") + self.gridLayout_7.addWidget(self.label_3, 6, 0, 1, 4) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem3, 5, 0, 1, 1) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem4, 1, 0, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout_7) + self.verticalLayout_2.addWidget(self.groupBox_5) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem5) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.groupBox_4 = QtWidgets.QGroupBox(self.tab) + self.groupBox_4.setObjectName("groupBox_4") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_4) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.gridLayout_8 = QtWidgets.QGridLayout() + self.gridLayout_8.setObjectName("gridLayout_8") + self.ram_usb_768 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_768.setObjectName("ram_usb_768") + self.gridLayout_8.addWidget(self.ram_usb_768, 2, 2, 1, 1) + self.ram_usb_256 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_256.setObjectName("ram_usb_256") + self.gridLayout_8.addWidget(self.ram_usb_256, 2, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(self.groupBox_4) + self.label_6.setObjectName("label_6") + self.gridLayout_8.addWidget(self.label_6, 0, 0, 1, 5) + self.ram_usb_1024 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_1024.setObjectName("ram_usb_1024") + self.gridLayout_8.addWidget(self.ram_usb_1024, 2, 3, 1, 1) + self.label_4 = QtWidgets.QLabel(self.groupBox_4) + self.label_4.setObjectName("label_4") + self.gridLayout_8.addWidget(self.label_4, 4, 0, 1, 4) + self.ram_usb_512 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_512.setObjectName("ram_usb_512") + self.gridLayout_8.addWidget(self.ram_usb_512, 2, 1, 1, 1) + self.boot_usb_qemu = QtWidgets.QPushButton(self.groupBox_4) + self.boot_usb_qemu.setObjectName("boot_usb_qemu") + self.gridLayout_8.addWidget(self.boot_usb_qemu, 4, 4, 1, 1) + self.ram_usb_2048 = QtWidgets.QRadioButton(self.groupBox_4) + self.ram_usb_2048.setObjectName("ram_usb_2048") + self.gridLayout_8.addWidget(self.ram_usb_2048, 2, 4, 1, 1) + spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem6, 1, 1, 1, 1) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem7, 3, 2, 1, 1) + spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem8, 5, 0, 1, 1) + self.verticalLayout_4.addLayout(self.gridLayout_8) + self.gridLayout_6.addWidget(self.groupBox_4, 0, 0, 1, 1) + self.verticalLayout_2.addLayout(self.gridLayout_6) + self.tabWidget.addTab(self.tab, "") + self.tab_2 = QtWidgets.QWidget() + self.tab_2.setObjectName("tab_2") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_2) + self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.gridLayout_10 = QtWidgets.QGridLayout() + self.gridLayout_10.setObjectName("gridLayout_10") + spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem9, 0, 1, 1, 1) + spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem10, 1, 0, 1, 1) + spacerItem11 = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem11, 2, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(self.tab_2) + self.label_5.setObjectName("label_5") + self.gridLayout_10.addWidget(self.label_5, 1, 1, 1, 1) + spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_10.addItem(spacerItem12, 1, 2, 1, 1) + self.horizontalLayout_6.addLayout(self.gridLayout_10) + self.tabWidget.addTab(self.tab_2, "") + self.horizontalLayout.addWidget(self.tabWidget) + + self.retranslateUi(Dialog) + self.tabWidget.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "multibootusb")) + self.label_persistence_value.setText(_translate("Dialog", "0 MB")) + self.groupBox_11.setTitle(_translate("Dialog", "Detect")) + self.detect_usb.setText(_translate("Dialog", "Detect Drives")) + self.create.setText(_translate("Dialog", "Create")) + self.labelstep1.setText(_translate("Dialog", "

Step 1

")) + self.labelstep2.setText(_translate("Dialog", "

Step 2

")) + self.close.setText(_translate("Dialog", "Close")) + self.labelstep3.setText(_translate("Dialog", "

Step 3

")) + self.slider_persistence.setToolTip(_translate("Dialog", "Choose Persistence size. Not all distros supports persistence...")) + self.groupBox.setTitle(_translate("Dialog", "Uninstall (Optional)")) + self.uninstall.setText(_translate("Dialog", "Uninstall Distro")) + self.groupBox_6.setTitle(_translate("Dialog", "USB Details")) + self.usb_dev.setText(_translate("Dialog", "Drive ::")) + self.usb_vendor.setText(_translate("Dialog", "Vendor ::")) + self.usb_model.setText(_translate("Dialog", "Model::")) + self.usb_size.setText(_translate("Dialog", "Size ::")) + self.usb_mount.setText(_translate("Dialog", "Mount ::")) + self.browse_iso.setText(_translate("Dialog", "Browse ISO")) + self.label_persistence.setText(_translate("Dialog", "

Persistence

")) + self.checkBox_all_drives.setText(_translate("Dialog", "All Drives")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Dialog", "MultiBootUSB")) + self.groupBox_7.setTitle(_translate("Dialog", "Imager")) + self.groupBox_9.setTitle(_translate("Dialog", "-------------- USB details -------------------")) + self.pushbtn_imager_refreshusb.setText(_translate("Dialog", "Refresh USB")) + self.imager_disk_label.setText(_translate("Dialog", "Disk Label ::")) + self.imager_total_size.setText(_translate("Dialog", "Disk Size")) + self.imager_uuid.setText(_translate("Dialog", "Disk Label ::")) + self.groupBox_10.setTitle(_translate("Dialog", "------------------------------ ISO details ----------------------------------")) + self.pushButton.setText(_translate("Dialog", "Browse ISO")) + self.imager_bootable.setText(_translate("Dialog", "Bootable ISO")) + self.imager_iso_size.setText(_translate("Dialog", "ISO Size")) + self.groupBox_8.setTitle(_translate("Dialog", "Imager Progress")) + self.imager_write.setText(_translate("Dialog", "Write/Create")) + self.imager_close.setText(_translate("Dialog", "Close")) + self.label_10.setText(_translate("Dialog", "

WARNING : Any bootable USB made using ISO Imager will destroy all data on the selected USB disk.

Use it at your own risk. Developers are not responsile for loss of any data.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.imager), _translate("Dialog", "ISO Imager")) + self.groupBox_2.setTitle(_translate("Dialog", "Install Syslinux")) + self.install_syslinux.setText(_translate("Dialog", "Install")) + self.install_sys_all.setText(_translate("Dialog", "Install syslinux and copy all required files.")) + self.install_sys_only.setText(_translate("Dialog", "Install only syslinux (existing configurations will not be altred).")) + self.groupBox_3.setTitle(_translate("Dialog", "Edit syslinux.cfg")) + self.edit_syslinux.setText(_translate("Dialog", "Edit")) + self.label_2.setText(_translate("Dialog", "

Using this option user can edit syslinux.cfg file directly. It directly uses

default editor of host system. Be careful while editing syslinux.cfg file.

")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.syslinux_ab), _translate("Dialog", "Syslinux")) + self.groupBox_5.setTitle(_translate("Dialog", "Boot ISO")) + self.ram_iso_2048.setText(_translate("Dialog", "2048 MB")) + self.ram_iso_1024.setText(_translate("Dialog", "1024 MB")) + self.ram_iso_256.setText(_translate("Dialog", "256 MB")) + self.browse_iso_qemu.setText(_translate("Dialog", "Browse ISO")) + self.label_7.setText(_translate("Dialog", "

Best way to test your downloaded ISOs.

")) + self.ram_iso_512.setText(_translate("Dialog", "512 MB")) + self.boot_iso_qemu.setText(_translate("Dialog", "Boot ISO")) + self.ram_iso_768.setText(_translate("Dialog", "768 MB")) + self.label_3.setText(_translate("Dialog", "Choose desired RAM and click on Boot ISO button.")) + self.groupBox_4.setTitle(_translate("Dialog", "Boot USB")) + self.ram_usb_768.setText(_translate("Dialog", "768 MB")) + self.ram_usb_256.setText(_translate("Dialog", "256 MB")) + self.label_6.setText(_translate("Dialog", "

Use this option if you want to check USB installation without reboot.

")) + self.ram_usb_1024.setText(_translate("Dialog", "1024 MB")) + self.label_4.setText(_translate("Dialog", "Choose desired RAM and click on Boot USB button.")) + self.ram_usb_512.setText(_translate("Dialog", "512 MB")) + self.boot_usb_qemu.setText(_translate("Dialog", "Boot USB")) + self.ram_usb_2048.setText(_translate("Dialog", "2048 MB")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "QEMU")) + self.label_5.setText(_translate("Dialog", "

An advanced bootable usb creator with option to install/uninstall

multiple distros. This software is written in python and pyqt.

Copyright 2010-2016 Sundar

Author(s): Sundar, Ian Bruce, Lee

Licence: GPL version 2 or later

Home page: http://multibootusb.org

Help/Email: feedback.multibootusb@gmail.com

Source Code:https://github.com/mbusb/multibootusb


")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "About")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Dialog = QtWidgets.QDialog() + ui = Ui_Dialog() + ui.setupUi(Dialog) + Dialog.show() + sys.exit(app.exec_()) + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/imager.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/imager.py new file mode 100644 index 0000000..0d3e006 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/imager.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: imager.py +# Purpose: Module to write ISO image to selected USB disk. Uses dd as backend. +# 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 +# WARNING : Any boot-able USB made using this module will destroy data stored on USB disk. + +import os +import subprocess +import collections +import platform +import signal +from PyQt5 import QtGui +from PyQt5 import QtWidgets +from PyQt5 import QtCore +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * +from . import iso +from . import usb +from . import config +from . import progressbar + +if platform.system() == "Windows": + import win32com.client + + +def dd_linux(): + import time + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link)) + output = "of=" + config.imager_usb_disk + os.system("umount " + config.imager_usb_disk + "1") + command = ['dd', input, output, "bs=1M"] + log("Executing ==> " + " ".join(command)) + dd_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while dd_process.poll() is None: + time.sleep(.1) # If this time delay is not given, the Popen does not execute the actual command + dd_process.send_signal(signal.SIGUSR1) + dd_process.stderr.flush() + while True: + out_error = dd_process.stderr.readline().decode() + if out_error: + if 'bytes' in out_error: + copied = int(out_error.split(' ', 1)[0]) + config.imager_percentage = round((float(copied) / float(in_file_size) * 100)) + pbar.update(config.imager_percentage) + break + + if dd_process.poll() is not None: + log("Executing ==> sync") + os.system("sync") + log("ISO has been written to USB disk...") + return + + +def dd_win(): + + windd = resource_path(os.path.join("data", "tools", "dd", "dd.exe")) + if os.path.exists(resource_path(os.path.join("data", "tools", "dd", "dd.exe"))): + log("dd exist") + input = "if=" + config.imager_iso_link + in_file_size = float(os.path.getsize(config.imager_iso_link) / 1024 / 1024) + output = "of=\\\.\\" + config.imager_usb_disk + command = [windd, input, output, "bs=1M", "--progress"] + log("Executing ==> " + " ".join(command)) + dd_process = subprocess.Popen(command, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, + shell=False) + while dd_process.poll() is None: + for line in iter(dd_process.stderr.readline, ''): + line = line.strip() + if 'error' in line.lower() or 'invalid' in line.lower(): + log("Error writing to disk...") + break + if line and line[-1] == 'M': + copied = float(line.strip('M').replace(',', '')) + config.imager_percentage = round((copied / float(in_file_size) * 100)) + + log("ISO has been written to USB disk...") + + return + + +class Imager(QtWidgets.QDialog, Ui_Dialog): + """ + Raw write to USB disk using dd. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + def on_Imager_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_3.clear() + config.imager_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + if config.imager_iso_link: + if platform.system() == "Windows": + if "/" in config.imager_iso_link: + config.imager_iso_link = config.imager_iso_link.strip().replace("/", "\\") + self.ui.lineEdit_3.insert(str(config.imager_iso_link)) + self.add_iso_gui_label_text() + else: + log("File not selected...") + + def add_iso_gui_label_text(self): + """ + Simple function to add text label to GUI widgets. + :return: + """ + log("Testing ISO...") + if iso.is_bootable(config.imager_iso_link) is True: + self.ui.imager_bootable.setText("Bootable ISO :: Yes") + log("ISO is bootable.") + else: + self.ui.imager_bootable.setText("Bootable ISO :: No") + log("ISO is not bootable.") + + if os.path.exists(config.imager_iso_link): + log("Path " + config.imager_iso_link + " is exist...") + self.iso_size = str(round(os.path.getsize(config.imager_iso_link) / 1024 / 1024)) + self.ui.imager_iso_size.setText("ISO Size :: " + self.iso_size + " MB") + log("ISO Size is " + self.iso_size + " MB") + + def onImagerComboChange(self): + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.imager_usb_disk): + self.ui.imager_disk_label.setText("Disk Type :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).usb_type) + self.ui.imager_total_size.setText("Disk Size :: " + usb.bytes2human(int(self.imager_usb_detail + (config.imager_usb_disk, + partition=0).total_size))) + if platform.system() == "Linux": + self.ui.imager_uuid.setText("Disk Model :: " + str(self.imager_usb_detail(config.imager_usb_disk, + partition=0).model)) + else: + self.ui.imager_uuid.setText("Disk Label :: " + self.imager_usb_detail(config.imager_usb_disk, + partition=0).model) + + def imager_list_usb(self, partition=1): + """ + Function to detect whole USB disk. It uses lsblk package on Linux. + :param partition: What to return. By default partition is set. + :return: USB disk/partition as list + """ + disk = [] + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -i", shell=True) + if not partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and (line[5].strip()) == b'disk': + disk.append(str("/dev/" + str(line[0].strip().decode()))) + elif partition == 1: + for line in output.splitlines(): + line = line.split() + if (line[2].strip()) == b'1' and line[5].strip() == b'part': + disk.append(str("/dev/" + str(line[0].strip()[2:]))) + else: + if partition == 1 or not partition == 1: + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + disk.append(drive) + return disk + + def imager_usb_detail(self, usb_disk, partition=1): + """ + Function to detect details of USB disk using lsblk + :param usb_disk: path to usb disk + :param partition: by default partition is set (but yet to code for it) + :return: details of size, type and model as tuples + """ + _ntuple_diskusage = collections.namedtuple('usage', 'total_size usb_type model') + + if platform.system() == "Linux": + output = subprocess.check_output("lsblk -ib " + usb_disk, shell=True) + for line in output.splitlines(): + line = line.split() + if not partition == 1: + if line[2].strip() == b'1' and line[5].strip() == b'disk': + total_size = line[3] + if not total_size: + total_size = "Unknown" + usb_type = "Removable" + model = subprocess.check_output("lsblk -in -f -o MODEL " + usb_disk, shell=True).decode() + if not model: + model = "Unknown" + else: + try: + selected_usb_part = str(usb_disk) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No label." + total_size = d.TotalSize + usb_type = "Removable" + model = label + except: + log("Error detecting USB details.") + + return _ntuple_diskusage(total_size, usb_type, model) + + def get_usb_size(self, usb_disk): + """ + Function to detect USB disk space. Useful but not used in multibootusb as of now. + :param usb_disk: USB disk like "/dev/sdb" + :return: Size of the disk as integer + """ + if platform.system() == "Linux": + cat_output = subprocess.check_output("cat /proc/partitions | grep " + usb_disk[5:], shell=True) + usb_size = int(cat_output.split()[2]) * 1024 + # log(usb_size) + return usb_size + else: + usb_size = self.usb.disk_usage(self.usb.get_usb(usb_disk).mount).total + return usb_size diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/install.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/install.py new file mode 100644 index 0000000..7c34d8b --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/install.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: install.py +# Purpose: This module contain functions to install ISO files to USB disk non destructively. +# 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 shutil +import sys +import platform +import threading +import subprocess +from .usb import * +from .gen import * +# from .iso import * +from . import iso +from scripts.update_cfg_file import * +from . import config +from . import persistence + + +def install_distro(): + """ + Install selected ISO to USB disk. + :return: + """ + usb_mount = config.usb_mount + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(config.iso_link)) + _iso_file_list = iso.iso_file_list(config.iso_link) + + if not os.path.exists(os.path.join(usb_mount, "multibootusb")): + log("Copying multibootusb directory to " + usb_mount) + shutil.copytree(resource_path(os.path.join("data", "tools", "multibootusb")), + os.path.join(config.usb_mount, "multibootusb")) + + if not os.path.exists(install_dir): + os.makedirs(install_dir) + with open(os.path.join(install_dir, "multibootusb.cfg"), "w") as f: + f.write(config.distro) + with open(os.path.join(install_dir, "iso_file_list.cfg"), 'w') as f: + for file_path in _iso_file_list: + f.write(file_path + "\n") + log("Installing " + iso_name(config.iso_link) + " on " + install_dir) + + if config.distro == "opensuse": + iso.iso_extract_file(config.iso_link, install_dir, 'boot') + status_text = "Copying ISO..." + if platform.system() == "Windows": + subprocess.call(["xcopy", config.iso_link, usb_mount], shell=True) # Have to use xcopy as python file copy is dead slow. + elif platform.system() == "Linux": + log("Copying " + config.iso_link + " to " + usb_mount) + shutil.copy(config.iso_link, usb_mount) + elif config.distro == "Windows" or config.distro == "alpine" or config.distro == 'pc-unlocker': + log("Extracting iso to " + usb_mount) + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == "trinity-rescue": + iso.iso_extract_file(config.iso_link, usb_mount, '*trk3') + elif config.distro == "ipfire": + iso.iso_extract_file(config.iso_link, usb_mount, '*.tlz') + iso.iso_extract_file(config.iso_link, usb_mount, 'distro.img') + iso.iso_extract_file(config.iso_link, install_dir, 'boot') + elif config.distro == "zenwalk": + config.status_text = "Copying ISO..." + iso.iso_extract_file(config.iso_link, install_dir, "kernel") + copy_iso(config.iso_link, install_dir) + elif config.distro == "salix-live": + # iso.iso_extract_file(config.iso_link, install_dir, "boot") + iso.iso_extract_file(config.iso_link, install_dir, '*syslinux') + iso.iso_extract_file(config.iso_link, install_dir, '*menus') + iso.iso_extract_file(config.iso_link, install_dir, '*vmlinuz') + iso.iso_extract_file(config.iso_link, install_dir, '*initrd*') + iso.iso_extract_file(config.iso_link, usb_mount, '*modules') + iso.iso_extract_file(config.iso_link, usb_mount, '*packages') + iso.iso_extract_file(config.iso_link, usb_mount, '*optional') + iso.iso_extract_file(config.iso_link, usb_mount, '*liveboot') + #iso.iso_extract_full(config.iso_link, usb_mount) + # config.status_text = "Copying ISO..." + # copy_iso(config.iso_link, install_dir) + elif config.distro == 'sgrubd2': + copy_iso(config.iso_link, install_dir) + elif config.distro == 'alt-linux': + iso.iso_extract_file(config.iso_link, install_dir, '-xr!*rescue') + iso.iso_extract_file(config.iso_link, config.usb_mount, 'rescue') + elif config.distro == "generic": + #with open(os.path.join(install_dir, "generic.cfg"), "w") as f: + # f.write(os.path.join(isolinux_bin_dir(config.iso_link), "generic") + ".bs") + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'grub4dos': + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'ReactOS': + iso_extract_full(config.iso_link, usb_mount) + elif config.distro == 'grub4dos_iso': + copy_iso(config.iso_link, install_dir) + else: + iso.iso_extract_full(config.iso_link, install_dir) + + if platform.system() == 'Linux': + log('ISO extracted successfully. Sync is in progress...') + os.system('sync') + + if config.persistence != 0: + log('Creating Persistence...') + config.status_text = 'Creating Persistence...' + persistence.create_persistence() + + install_patch() + + +def copy_iso(src, dst): + """ + A simple wrapper for copying larger files. This is necessary as + shutil copy files is much slower under Windows platform + :param src: Path to source file + :param dst: Destination directory + :return: + """ + if platform.system() == "Windows": + subprocess.call("xcopy " + src + " " + dst, shell=True) + elif platform.system() == "Linux": + shutil.copy(src, dst) + +def install_progress(): + """ + Function to calculate progress percentage of install. + :return: + """ + from . import progressbar + + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + usb_size_used = usb_details['size_used'] + thrd = threading.Thread(target=install_distro, name="install_progress") + # thrd.daemon() + # install_size = usb_size_used / 1024 + install_size = iso_size(config.iso_link) / 1024 + final_size = (usb_size_used + iso_size(config.iso_link)) + config.persistence + thrd.start() + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = details(config.usb_disk)['size_used'] + percentage = int((current_size / final_size) * 100) + if percentage > 100: + percentage = 100 + config.percentage = percentage + pbar.update(percentage) + + +def install_patch(): + """ + Function to certain distros which uses makeboot.sh script for making bootable usb disk. + This is required to make sure that same version (32/64 bit) of modules present is the isolinux directory + :return: + """ + if config.distro == 'debian': + if platform.system() == 'Linux': # Need to syn under Linux. Otherwise, USB disk becomes random read only. + os.system('sync') + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(config.iso_link)) + iso_linux_bin_dir = isolinux_bin_dir(config.iso_link) + config.syslinux_version = isolinux_version(isolinux_path) + iso_file_list = iso.iso_file_list(config.iso_link) + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link)) + if any("makeboot.sh" in s.lower() for s in iso_file_list): + for module in os.listdir(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link))): + if module.endswith(".c32"): + if os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module)): + try: + os.remove(os.path.join(config.usb_mount, "multibootusb", + iso_basename(config.iso_link), isolinux_bin_dir(config.iso_link), module)) + log("Copying " + module) + log((resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module))) + shutil.copy(resource_path( + os.path.join(multibootusb_host_dir(), "syslinux", "modules", config.syslinux_version, module)), + os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link), + isolinux_bin_dir(config.iso_link), module)) + except Exception as err: + log(err) + log("Could not copy " + module) + else: + log('Patch not required...') + + +if __name__ == '__main__': + config.iso_link = '../../../DISTROS/2016/slitaz-4.0.iso' + install_distro() diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/iso.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/iso.py new file mode 100644 index 0000000..805e9e3 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/iso.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: iso.py +# Purpose: Module to manupulate ISO file +# Authors: Sundar +# Depends: isodump3.py (Authored by Johni Lee for MultiBootUSB) +# 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 sys +import os +import string +import platform +import re +from .gen import * +from .isodump3 import ISO9660 +from . import _7zip + + +_iso_cfg_ext_dir = iso_cfg_ext_dir() + + +def iso_name(iso_link): + """ + Find the name of an ISO. + :return: Name of an ISO (with extension) as string. Returns If not returns None. + """ + if os.path.exists(iso_link): + try: + name = os.path.basename(str(iso_link)) + except: + name = None + else: + name = None + + return name + + +def iso_basename(iso_link): + """ + Find the base name of an ISO. + :return: Base name (without extension) of a selected ISO as string. If not returns None. + """ + try: + dir_name = str(os.path.splitext(os.path.basename(str(iso_link)))[0]) + except: + dir_name = None + + return dir_name + + +def isolinux_bin_exist(iso_link): + """ + Check if an "isolinux.bin" file exist. + :return: True if "isolinux.bin" file exist of False if not. + """ + if os.path.exists(iso_link): + iso_file_list = _7zip.list_iso(iso_link) + if any("isolinux.bin" in s.lower() for s in iso_file_list): + return True + else: + return False + + + +def iso_size(iso_link): + return os.path.getsize(iso_link) + + +def is_bootable(iso_link): + """ + Check if an ISO has the ability to boot. + :return: True if ISO is bootable and False if not. + """ + iso9660fs = ISO9660(iso_link) + isBootable = iso9660fs.checkISOBootable() + if isBootable: + return True + else: + return False + + +def isolinux_bin_dir(iso_link): + """ + Detects "isolinux.bin" directory. + :return: path of "isolinux.bin" directory as string. + """ + if os.path.exists(iso_link): + bin_dir = False + iso_file_list = _7zip.list_iso(iso_link) + if any("isolinux.bin" in s.lower() for s in iso_file_list): + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + if 'efi' not in f.lower(): # Certain distros place their isolinux.bin in to /EFI/BOOT director and we don't want to include them + bin_dir = os.path.dirname(f) + break + + return bin_dir + + +def isolinux_bin_path(iso_link): + """ + Detects pat to "isolinux.bin". + :return: path of "isolinux.bin" as a string. + """ + iso_bin_path = False + if isolinux_bin_exist(iso_link) is not False: + iso_file_list = _7zip.list_iso(iso_link) + for f in iso_file_list: + if 'isolinux.bin' in f.lower(): + iso_bin_path = f + break + + return iso_bin_path + + +def iso_menu_lst_path(iso_link): + """ + Detects pat to "menu.lst" of grub4dos. + :return: path of "menu.lst" as a string. + """ + menu_lst_path = False + iso_file_list = _7zip.list_iso(iso_link) + for f in iso_file_list: + if 'menu.lst' in f.lower(): + menu_lst_path = f + break + + return menu_lst_path + + +def integrity(iso_link): + """ + Check the integrity of an ISO. + :return: True if integrity passes or False if it fails. + """ + return _7zip.test_iso(iso_link) + + +def iso_file_list(iso_link): + """ + Function to return the content of an ISO. + :return: List of files of an ISO as list. + """ + return _7zip.list_iso(iso_link) + + +def isolinux_version(isolinux_bin_path): + """ + Detect isolinux version shipped by distros. + :param isolinux_path: Path to "isolinux.bin" + :return: Version number as string. + """ + version = ["3", "4", "5", "6"] + if isolinux_bin_path is not None: + sl = list(strings(isolinux_bin_path)) + for strin in sl: + if re.search(r'isolinux ', strin, re.I): + for number in version: + if re.search(r'isolinux ' + number, strin, re.I): + log("\n\nFound syslinux version " + number + "\n\n") + return str(number) + + +def iso_extract_file(iso_link, dest_dir, filter): + """ + Extract the specific file(s) from an ISO + :param dest_dir: Path to destination directory. + :param filter: Filter to extract particular file(s) + :return: Extract file(s) to destination. + """ + _7zip.extract_iso(iso_link, dest_dir, filter) + + +def extract_cfg_file(iso_link): + """ + Function to extract certain files for auto detecting supported distros + :param iso_link: Path to ISO file + :return: + """ + _pattern = ['.cfg', '.CFG', '.txt', '.TXT', 'isolinux.bin', 'ISOLINUX.BIN', '.lst'] + # file_list = iso_file_list(iso_link) + for ext in _pattern: + _7zip.extract_iso(iso_link, _iso_cfg_ext_dir, pattern='*' + ext) + + +def iso_extract_full(iso_link, dest_dir): + """ + Extract an ISO to destination directory + :param dest_dir: Destination path as string. + :return: False if it fails or extract ISO files to destination directory. + """ + _7zip.extract_iso(iso_link, dest_dir) + + +if __name__ == '__main__': + #iso_path = '../../../DISTROS/2016/debian-live-8.3.0-amd64-lxde-desktop.iso' + iso_path = '../../../DISTROS/2015/super_grub2_disk_hybrid_2.02s3.iso' + test_iso_bin_path = os.path.join('test', 'isolinux', 'isolinux.bin') + log('iso_name(iso_path) : ', iso_name(iso_path)) + log('iso_basename(iso_path) : ', iso_basename(iso_path)) + log('Integrity of ISO is : ', integrity(iso_path)) + f_list = (iso_file_list(iso_path)) + if f_list: + for f in f_list: + log(f) + log('isolinux_bin_exist(iso_path) : ', isolinux_bin_exist(iso_path)) + #log('is_bootable : ', is_bootable(iso_path)) + log('isolinux_bin_dir() : ', isolinux_bin_dir(iso_path)) + log('isolinux_bin_path(iso_path) : ', isolinux_bin_path(iso_path)) + iso_extract_full(iso_path, 'test') + iso_extract_file(iso_path, 'test', 'isolinux.bin') + log(isolinux_version(test_iso_bin_path)) + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/isodump3.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/isodump3.py new file mode 100644 index 0000000..5e572ec --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/isodump3.py @@ -0,0 +1,806 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +# Name: isodump3.py +# Purpose: Module to list and extract iso files. +# Authors: LiQiong Lee (written exclusively for multibootusb) +# Licence: This file is a part of multibootusb package. You can redistribute it or modify +# under the terms of GNU General Public License version 3 +# Credit : I am grateful to LiQiong Lee. He not only wrote this module for multibootusb, but also extended the same +# to python3 within short time after request. + +""" ISO9660fs +Dump raw meta data of iso9660 file system. +Extract directories and files. +""" +## +## Extract directory or file from iso. +## Support RRIP. +## + +# Author : joni +# version : 1.0 + +import sys +import struct +import os +import re +import stat +from ctypes import * +from . import config +from . import gen + + +BLOCK_SIZE = 2048 +S_IFSOCKET = 0o140000 +S_IFLINK = 0o120000 +S_IFREG = 0o100000 +S_IFBLK = 0o060000 +S_IFCHR = 0o020000 +S_IFDIR = 0o040000 +S_IFIFO = 0o010000 + +E_SUCCESS = 0 +E_FAILURE = -1 +E_DEVICEFILE = -2 # can't write device file + +class PrimaryVolume(Structure): + def __init__(self): + self.sysIdentifier = "" + self.volIdentifier = "" + self.volSize = 0 + self.volSeq = 0 + self.blockSize = 0 + self.ptSize = 0 + self.ptLRd = 0 + self.fsVer = 0 + self.rootLoc = 0 + self.rootTotal = 0 + +class Rrip(Structure): + def __init__(self): + self.offset = -1 + self.altname = "" + self.devH = 0 + self.devL = 0 + self.fMode = 0 + +class DirRecord(Structure): + def __init__(self): + self.lenDr = 0 + self.lenEattr = 0 + self.locExtent= 0 + self.lenData = 0 + self.dtYear = 0 + self.dtMonth = 0 + self.dtHour = 0 + self.dtMinute = 0 + self.dtSecond = 0 + self.dtOffset = 0 + self.fFlag = 0 + self.fUnitSize= 0 + self.gapSize = 0 + self.volSeqNr = 0 + self.lenFi = 0 + self.fIdentifier = "" + self.sysUseStar = 0 + self.suspBuf = "" + self.rrip = None + +class PathTabelItem(Structure): + def __init__(self): + self.lenDi = 0 + self.lenEattr = 0 + self.locExtenti = 0 + self.pdirNr = 0 + self.fIdentifier = "" + +class ISO9660: + """ + This class can dump iso9660 file system meta data and extract files. + Support: + RRIP extension. + """ + + def __init__(self, isofile): + try: + f = open(isofile, 'rb') + except(IOError): + sys.stderr.write("can't open {0}".format(isofile)) + sys.exit(-1) + + self.isoFile = f + self.priVol = None + self.rootDir = None + self.rripOffset = -1 + + desc_nr = 0 + while True: + desc_nr = desc_nr + 1 + try: + self.isoFile.seek(BLOCK_SIZE*(15+desc_nr)) + volume_dsc = self.isoFile.read(BLOCK_SIZE) + flag = struct.unpack('B',volume_dsc[0:1])[0] + if flag == 1: + self.__readPrimaryVolume__(volume_dsc) + continue + if flag == 255: + break + except Exception as e: + gen.log("Got exception when init iso file:", sys.exc_info()[0]) + self.priVol = None + self.rootDir = None + break + + def __del__(self): + self.isoFile.close() + + def __readPrimaryVolume__(self, volume_dsc): + """ Dump primary volume descriptor """ + global BLOCK_SIZE + priVol = PrimaryVolume() + priVol.sysIdentifier = volume_dsc[8:40] + priVol.volIdentifier = volume_dsc[40:72] + priVol.volSize = struct.unpack(' 0: + entry_buf = desc_buf[self.rripOffset:] + gen.log ("__rripLoop__ offset:%d"%(self.rripOffset)) + else: + entry_buf = desc_buf + + rr = Rrip() + while True: + ce_blk = 0 + ce_len = 0 + ce_off = 0 + head = 0 + len_entry = 0 + + while True: + #gen.log (("\n%d, %d\n")%(len_buf, head)) + head += len_entry + if len_buf - head < 4: # less than one entry + break + entry_buf = entry_buf[len_entry:] + + sig1 = struct.unpack("B", entry_buf[0:1])[0] + sig2 = struct.unpack("B", entry_buf[1:2])[0] + len_entry = struct.unpack("B", entry_buf[2:3])[0] + ver = struct.unpack("B", entry_buf[3:4])[0] + #if len_entry == 0: + # gen.log "Got a entry in __rripLoop__ (%c,%c) of SUSP with length:(%d),version:(%d)-->"%(sig1,sig2,len_entry, ver), + if len_entry == 0: + break; + + if sig1 == ord('S') and sig2 == ord('P'): + ck1 = struct.unpack("B", entry_buf[4:5])[0] + ck2 = struct.unpack("B", entry_buf[5:6])[0] + skip = struct.unpack("B", entry_buf[6:7])[0] + #gen.log "-->(0x%x==0xBE,0x%x==EF,%d)" %(ck1, ck2, skip) + if ck1 == 0xBE and ck2 == 0xEF: + rr.offset = skip + continue + + if sig1 == ord('C') and sig2 == ord('E'): + ce_blk = struct.unpack("(%d,%d,%d)" %(ce_blk, ce_off, ce_len) + continue + + if sig1 == ord('N') and sig2 == ord('M'): + flag = struct.unpack("B", entry_buf[4:5])[0] + #gen.log "-->(flag:(0x%x), name:(%s))" %(flag, entry_buf[5:len_entry]) + if flag == 0x02: # FLAG_CURRENT + rr.altname += "." + elif flag == 0x04: # FLAG_PARENT + rr.altname += ".." + elif flag == 0x01 or flag ==0: # 1:FLAG_CONTINUE + rr.altname += entry_buf[5:len_entry].decode() + continue + + if sig1 == ord('P') and sig2 == ord('N'): + rr.devH = struct.unpack(" 0: + #gen.log " Read CE block, (%d, %d, %d)"%(ce_blk, ce_len, ce_off) + self.isoFile.seek(ce_blk*BLOCK_SIZE + ce_off) + entry_buf = self.isoFile.read(ce_len) + len_buf = ce_len + else: + break + # while (True) end # + return rr + + def checkISOBootable(self): + """ Struct of a classical generic MBR. + + 0x0000 Bootstrap Code area + ----------------------------------------- + 0x01BE + .. Partition table + 0x01EE + ------------------------------------------ + 0x01FE 55h + Boot signature + 0x01FF AAh + + """ + self.isoFile.seek(0x01FE) + h = self.isoFile.read(2) + s1 = struct.unpack('B', h[0:1])[0] + s2 = struct.unpack('B', h[1:2])[0] + + #gen.log "-->(0x%x,0x%x)" %(s1, s2) + + if (s1 == 0x55) and (s2 == 0xAA): + result = True # "Bootable" + else: + result = False # "Not bootable" + + return result + + def searchDir(self, path): + # /root/abc/ - ['', 'root', 'abc', ''] + # /root/abc - ['', 'root', 'abc'] + # / - ['', ''] + dircomps = path.split('/') + if dircomps[-1] == '': + dircomps.pop() + if dircomps == []: + return + + if self.priVol == None: + return + + if len(dircomps) == 1: + return self.rootDir + + pdir_loc = self.priVol.rootLoc + pdir_len = self.priVol.rootTotal + i_dircomp = 1 + + while True: + found = False + dirs = self.readDirItems(pdir_loc, pdir_len) + for item in dirs: + if item.fIdentifier == dircomps[i_dircomp]: + pdir_loc = item.locExtent + pdir_len = item.lenData + found = True + #gen.log "found (%s)"%(dircomps[i_dircomp]) + break + if found: # advacne + if i_dircomp < len(dircomps)-1: + i_dircomp = i_dircomp + 1 + else: + return item + else: + gen.log ("can't find " + dircomps[i_dircomp]) + return None + + def readDirrecord(self, desc_buf): + """ Dump file dirctory record + Return a directory record reading from a Directory Descriptors. + """ + dirRec = DirRecord() + try: + dirRec.lenDr = struct.unpack("B", desc_buf[0:1])[0] + if dirRec.lenDr == 0: + return None + except: + return None + + dirRec.lenEattr = struct.unpack("B", desc_buf[1:2])[0] + dirRec.locExtent = struct.unpack(" dirRec.sysUseStar+4: + if dirRec.locExtent == self.priVol.rootLoc: + dirRec.suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + suspBuf = desc_buf[dirRec.sysUseStar:dirRec.lenDr] + if self.rripOffset != -1: + rripNode = self.__rripLoop__(suspBuf, dirRec.lenDr-dirRec.sysUseStar) + dirRec.rrip = rripNode + if rripNode != None: + if rripNode.altname != "": + dirRec.fIdentifier = rripNode.altname + dirRec.lenFi = len(rripNode.altname) + #gen.log "rrip_altname: %s"%(dirRec.fIdentifier) + # if rripNode end # + # if self.rripOffset != -1 end # + # if dirRec.lenDr > .. end # + return dirRec + + def readDirItems(self, block_nr=None, total=None): + """ Read file dirctory records + Read dirctory records from 'block_nr' with a length of 'total'. + Return a list containing directory records(DirRecord). + """ + dirs = [] + total_blk = (total+BLOCK_SIZE-1)//BLOCK_SIZE + i_blk = 0 + while i_blk < total_blk: + self.isoFile.seek((block_nr+i_blk)*BLOCK_SIZE) + desc_buf = self.isoFile.read(BLOCK_SIZE) + i_blk = i_blk + 1 + while True: + dirItem = self.readDirrecord(desc_buf) + if dirItem == None: + break + + dirs.append(dirItem) + if desc_buf.__len__() > dirItem.lenDr: + desc_buf = desc_buf[dirItem.lenDr:] + else: + break + return dirs + + def readPathtableL(self): + """ Read path table of L typde """ + if self.priVol == None: + return + block_nr = self.priVol.ptLRd + total = self.priVol.ptSize + + path_table = [] + self.isoFile.seek(block_nr*BLOCK_SIZE) + ptbuf = self.isoFile.read((BLOCK_SIZE * ((total+BLOCK_SIZE-1)//BLOCK_SIZE))) + i = 0 + r_size = 0 + while True : + i = i+1 + t = PathTabelItem() + + t.lenDi = struct.unpack('B', ptbuf[0:1])[0] + t.lenEattr = struct.unpack('B', ptbuf[1:2])[0] + t.locExtent = struct.unpack('= total: + break + ptbuf = ptbuf[9+t.lenDi-1+len_pd:] + # while True + return path_table + + # @path -- path within iso file system. + # @output -- what local path you want write to. + # @pattern -- regular expression. + # @r -- recursion flag, write the whole sub-directories or not. + # @all_type -- which file type should be writed. + # False: Write regular type files only. + # True: Wirte all types files (regular, device file, link, socket, etc) + def writeDir(self, path, output, pattern="", r=True, all_type=False): + """ Extract a directory + Return 0 means success otherwise failure. + """ + d = self.searchDir(path) + if d != None: + if output.endswith("/"): + output = output[0:-1] + # Try to make target directory. + if not os.path.exists(output): + try: + os.makedirs(output) + except(OSError): + sys.stderr.write("can't make dirs({0})\n".format(output)) + return E_FAILURE + pp = None + if pattern != "": + p = r'{0}'.format(pattern) + pp = re.compile(p) + #gen.log "writeDir: flag(%x)"%(d.fFlag) + if d.fFlag & 0x02 == 0x02: + # Check if a clean directory. + #try: + # if len(os.listdir(output)) > 0: + # sys.stderr.write("The target directory is not empty\n") + # return E_FAILURE + #except(OSError): + # sys.stderr.write("can't access dirs({0})\n".format(p)) + # return E_FAILURE + self.writeDir_r(output, d, pp, r, all_type) + return E_SUCCESS + else: + return self.writeFile(d, output+path, all_type) + else: + return E_FAILURE + + def writeDir_r(self, det_dir, dire, pp, r, all_type): + #gen.log "writeDir_r:(%s)"%(det_dir) + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + if (pp != None) and (pp.search(d.fIdentifier) == None): + match = False + else: + match = True + #gen.log "mathing %s, %s, (%x)"%(match, d.fIdentifier, d.fFlag) + p = det_dir + "/" + d.fIdentifier + if d.fFlag & 0x02 == 0x02: + if not os.path.exists(p): + os.makedirs(p, 0o777) + if r: + if match: + self.writeDir_r(p, d, None, r, all_type) # Don't need to match subdirectory. + else: + self.writeDir_r(p, d, pp, r, all_type) + elif match: + self.writeFile(d, p, all_type) + # if not d.fIdentifier end # + # for d in dirs end # + + def writeFile(self, dirRec, detFile, all_type): + """ Write a file to detFile + Return 0 means success otherwise failure. + """ + global file_out + if detFile == "" or dirRec == None: + sys.stderr.write("can't write file\n") + return E_FAILURE + + #gen.log "write file (%s)"%(detFile) + config.status_text = detFile + + dirname = os.path.dirname(detFile) + if not os.path.exists(dirname): + try: + os.makedirs(dirname, 0o777) + except(OSError): + sys.stderr.write("can't makedirs\n") + return E_FAILURE + + if all_type == True: + # device file + if dirRec.rrip != None and (dirRec.rrip.devH != 0 or dirRec.rrip.devL != 0): + #fFlag == 0 + high = dirRec.rrip.devH + low = dirRec.rrip.devL + if high == 0: + device = os.makedev(os.major(low), os.minor(low)) + else: + device = os.makedev(high, os.minor(low)) + try: + mode = dirRec.rrip.fMode & 0o770000 + if mode == S_IFCHR: + os.mknod(detFile, 0o777|stat.S_IFCHR, device) + elif mode == S_IFBLK: + os.mknod(detFile, 0o777|stat.S_IFBLK, device) + except(OSError): + sys.stderr.write("can't mknode, maybe no permission\n") + return E_DEVICEFILE + + return E_SUCCESS + + loc = dirRec.locExtent + length = dirRec.lenData + self.isoFile.seek(BLOCK_SIZE * loc) + #gen.log "file length(%d)"%(length) + r_size = BLOCK_SIZE*1024*50 #100M cache + + try: + f_output = open(detFile, 'wb', r_size) + except(IOError): + sys.stderr.write("can't open{0} for write\n".format(detFile)) + return E_FAILURE + + while True: + if length == 0: + break + elif length <= r_size: + r_size = length + length = 0 + else: + length = length - r_size + + buf = self.isoFile.read(r_size) + f_output.write(buf) + f_output.flush() + # while True end. + f_output.close() + return E_SUCCESS + + def readDir(self, dir_path, r=True): + file_list = [] + d = self.searchDir(dir_path) + if d != None: + if (d.fFlag & 0x02) == 0x02: + #gen.log "readDir (%x, %x)"%(d.locExtent, d.lenData) + if dir_path.endswith("/"): + dir_path = dir_path[0:-1] + self.readDir_r(file_list, dir_path, d, r) + # if (d.fFlag & 0x02) == 0x02: # + # if d != None: + return file_list + + def readDir_r(self, file_list, dir_path, dire, r): + if (dire.fFlag & 0x02) != 0x02: + return + dirs = self.readDirItems(dire.locExtent, dire.lenData) + for d in dirs: + if not d.fIdentifier in [".", ".."]: + p = dir_path + "/" + d.fIdentifier + file_list.append(p) + if r: + self.readDir_r(file_list, p, d, r) + # if not d.fIdentifier # + # for d in dirs: # + + def checkIntegrity(self): + if self.priVol == None: # no primary volume + return False + + if self.priVol.ptSize == 0: # empty ? + return True + + path_table = self.readPathtableL() + if path_table == []: # pathtable record is broken. + return False + + # find last file item to check + for dr in reversed(path_table): + #gen.log dr.fIdentifier + dirs = self.readDirItems(dr.locExtent, BLOCK_SIZE) + if len(dirs) > 2: + dot = dirs[0] + dirs2 = self.readDirItems(dot.locExtent, dot.lenData) # get the whole items. + for dr2 in reversed(dirs2): # search last file item. + if dr2.fFlag == 0: + #gen.log "get last file(%s)"%(dr2.fIdentifier) + try: + #self.isoFile.seek(BLOCK_SIZE * dr2.locExtent+dr2.lenData) + lastfile_end = BLOCK_SIZE * dr2.locExtent + dr2.lenData + self.isoFile.seek(0, os.SEEK_END) + iso_end = self.isoFile.tell() + #gen.log ("%d-->%d")%(lastfile_end, iso_end) + if iso_end >= lastfile_end: + return True + else: + return False + except(IOError): + #gen.log "exception when seek. iso is broken" + return False + elif len(dirs) < 2: # Dir record is broken. At least, should have two entries. + return False + return True + +########################################################################### +def dump_dir_record(dirs): + """ Dump all the file dirctory records contained in desc_buf """ + + gen.log("Dump file/deirectory record") + gen.log("===========================", end="\n") + if dirs != None: + for f in dirs: + gen.log("length of directory record:(0x%x), length of extend attribute:(%d), \ +location of record:(%d)BLOCK->(0x%x), data length(%d) size of file unit:(%d), \ +interleave gap size:(%d), file flag:(0x%x),name length:(%d) identify:(%s)\n" \ +%(f.lenDr, f.lenEattr, f.locExtent, f.locExtent*BLOCK_SIZE,f.lenData, \ + f.fUnitSize, f.gapSize, f.fFlag, f.lenFi, f.fIdentifier)) + +def dump_pathtable_L(path_table): + """ Dump path table of L typde """ + + gen.log("Dump path table") + gen.log("================", end="\n") + #path_table = readPathtableL() + i = 0 + for t in path_table: + i = i + 1 + if t.lenDi == 1: + if t.fIdentifier in [0, 1]: + gen.log("is a root directory(%d)" %(is_root)) + gen.log("%d->length of identify:(%d), length of extend attribute:(%d), \ +local:(%d)->(0x%x), parent dir number:(%d), identify:(%s)\n" \ +%(i, t.lenDi, t.lenEattr, t.locExtent, t.locExtent*BLOCK_SIZE, t.pdirNr, t.fIdentifier)) + +def dump_primary_volume(privol=None): + """ Dump primary volume descriptor """ + + if privol == None: + gen.log("Can't dump, maybe iso is broken") + return + gen.log("===== Dump primary volume descriptor ==") + + gen.log("System Identifier:(%s)" %(privol.sysIdentifier.decode())) + gen.log("Volume Identifier:(%s)" %privol.volIdentifier.decode()) + gen.log("Volume Space size:(0x%x)BLOCKS(2kB)" %privol.volSize) + gen.log("Volume sequence number:(%d)" %(privol.volSeq)) + gen.log("logic block size:(0x%x)" %(privol.blockSize)) + gen.log("Volume path talbe L's BLOCK number is :(0x%x-->0x%x), size(%d)" %(privol.ptLRd, privol.ptLRd*BLOCK_SIZE, privol.ptSize)) +# gen.log "Abstract File Identifier: (%s)" %(volume_dsc[739:776]) +# gen.log "Bibliographic File Identifier: (%s)" %(volume_dsc[776:813]) + gen.log("pathtable locate (%d)" %(privol.ptLRd)) + gen.log("File Structure Version:(%d)" %(privol.fsVer)) + gen.log("Root directory is at (%d)block, have(0x%x)bytes" %(privol.rootLoc, privol.rootTotal)) +# dump_dir_record(None, 23, 1) + +def dump_boot_record(volume_dsc): + """ Dump boot record """ + + gen.log("===== Dump boot record ==") + std_identifier = volume_dsc[1:6] + gen.log("Standard Identifier:(%s)" %std_identifier) + + vol_ver = struct.unpack('B', volume_dsc[6]) + gen.log("Volume descriptor version:(%d)" %vol_ver) + + bootsys_identifier = volume_dsc[7:39] + gen.log("boot system identifier(%s)" %bootsys_identifier) + + boot_identifier = volume_dsc[39:71] + gen.log("boot identifier(%s)" %boot_identifier) + +def usage(): + """ Prompt user how to use """ + gen.log(""" +Usage: isodump dump-what [options] iso-file + [dump-what] + ----------- + boot - Dump boot record. + primary-volume - Dump primary volume. + pathtable - Dump path table. + dir-record [block number] [length] - Dump a raw data of a Directory Record + + iso:/dir [-r] [-o output] [-p pattern] - Dump a dirctory or file to [output] + -r recursively visit directory. + -p spcify a Regular expression pattern for re.search(pattern,). + +isodump xx.iso - Dump the root directory +isodump pathtable xx.iso - Dump the path table record. + +isodump iso:/ -r xx.iso + -- Dump the root directory of xx.iso recursively. + +isodump iso:/ -r -o /tmp/iso xx.iso + -- Extract the iso to /tmp/iso/. + +isodump iso:/boot -o /tmp/iso/boot xx.iso + -- Extract the /boot directory of xx.iso to /tmp/iso/boot. + +isodump iso:/boot/grup.cfg -o /tmp/grub.cfg xx.iso + -- Extract the file "grup.cfg" to "/tmp/grub.cfg" + +isodump iso:/boot -r -o /tmp/iso -p "*.cfg" xx.iso + -- Extract any files or directories under /boot maching "*.cfg" to /tmp/iso/. +""") + sys.exit(-1) + +if __name__ == '__main__': + argv = sys.argv + if len(argv) < 3: + usage() + + iso9660fs = ISO9660(argv[-1]) + integrity = iso9660fs.checkIntegrity() + if integrity == False: + gen.log("iso file is broken") + sys.exit(-1) + + dump_what = argv[1] + + if dump_what == "primary-volume": + dump_primary_volume(iso9660fs.priVol) + elif dump_what == "pathtable": + path_table = iso9660fs.readPathtableL() + dump_pathtable_L(path_table) + if dump_what == "dir-record": + if len(argv) == 5: + gen.log("dump dir-record (%s, %s)"%(argv[2], argv[3])) + dirs = iso9660fs.readDirItems(int(argv[2]), int(argv[3])) + dump_dir_record(dirs) + else: + usage() + elif dump_what.startswith("iso:"): + o_path = "" + r = False + o = False + p = False + pattern = "" + for arg in argv[2:-1]: + if arg == "-r": + r = True + o = False + p = False + elif arg == "-o": + o = True + p = False + elif arg == "-p": + o = False + p = True + elif o == True: + o_path = arg + o = False + elif p == True: + pattern = arg + p = False + + isodir = dump_what[4:] + if o_path == "": + gen.log("dump_dir(%s)"%(isodir)) + filelist = iso9660fs.readDir(isodir, r) + if filelist == []: + gen.log("can't read any file from (%s)"%(isodir)) + else: + for f in filelist: + gen.log(f) + else: + gen.log("writeDir(%s)->(%s) with pattern(%s)"%(isodir, o_path, pattern)) + sys.exit(iso9660fs.writeDir(isodir, o_path, pattern, r, True)) + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_cli.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_cli.py new file mode 100644 index 0000000..c8ef675 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_cli.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_cli.py +# Purpose: Module to handle command line options of multibootusb +# 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 re +import shutil +from . import usb +from . import gen +from .iso import * +from .uninstall_distro import * +from .distro import * +from .syslinux import * +from .install import * + + +def read_input_uninstall(): + response = False + try: + response = int(input("Please enter the number against the distro you need to uninstall: ")) + except ValueError: + log('\nPlease provide valid integer from the above list.\n') + + return response + + +def cli_install_distro(): + ''' + if platform.system() == 'Linux': + if os.getuid() != 0: + exit("You need to have root privileges to run this script.\nPlease try again using 'sudo'. Exiting.") + elif platform.system() == 'Windows': + + if admin.isUserAdmin(): + admin.elevate() + ''' + + log('Starting multibootusb from Command line...') + if usb.is_block(config.usb_disk) is False: + log(config.usb_disk, 'is not a valid device partition...') + exit(1) + elif integrity(config.iso_link) is not True: + log(config.iso_link, ' failed to pass integrity check...') + exit(1) + elif size_not_enough(config.iso_link, config.usb_disk) is True: + log(config.usb_disk, 'does not have enough space...') + else: + prepare_mbusb_host_dir() + extract_cfg_file(config.iso_link) + _distro = distro(iso_cfg_ext_dir(), config.iso_link) + log('Detected distro type is', _distro) + if _distro is not None: + log('\nSelected ISO is :', quote(iso_name(config.iso_link))) + log('Selected target device is:', quote(config.usb_disk), '\n') + log('Please confirm the option.') + log('Y/y/Yes/yes/YES or N/n/No/no/NO') + if read_input_yes() is True: + config.distro = _distro + copy_mbusb_dir_usb(config.usb_disk) + install_progress() + syslinux_distro_dir(config.usb_disk, config.iso_link, _distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, _distro) + else: + log('Sorry', iso_name(config.iso_link), 'is not supported at the moment\n' + 'Please report tissue at https://github.com/mbusb/multibootusb/issues') + + +def cli_uninstall_distro(): + distro_list = install_distro_list() + if distro_list is not None: + for index, _distro_dir in enumerate(distro_list): + log(index, '--->>', _distro_dir) + user_input = read_input_uninstall() + if user_input is not False: + for index, _distro_dir in enumerate(distro_list): + if index == user_input: + config.uninstall_distro_dir_name = _distro_dir + unin_distro() + else: + log('No distro installed on', config.usb_disk) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_gui.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_gui.py new file mode 100644 index 0000000..ed0e5ba --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/mbusb_gui.py @@ -0,0 +1,640 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: mbusb_gui.py +# Purpose: Module to handle multibootusb through gui +# 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 platform +import sys +import signal +from PyQt5 import QtCore, QtGui, QtWidgets +import subprocess +import time +from scripts.gui.ui_multibootusb import Ui_Dialog +from . import usb +from .gen import * +from .install import * +from .uninstall_distro import * +from .syslinux import * +from .distro import * +from .iso import * +from .imager import Imager, dd_linux, dd_win +from . import persistence +from . import config +from . import admin +from . import qemu +from .update_cfg_file import update_distro_cfg_files + + +class AppGui(qemu.Qemu, Imager, QtWidgets.QDialog, Ui_Dialog): + """ + Main multibootusb GUI manipulation class. + """ + + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + # Main Tab + self.ui.checkBox_all_drives.clicked.connect(self.add_device) + self.ui.detect_usb.clicked.connect(self.onRefereshClick) + self.ui.close.clicked.connect(self.on_close_Click) + self.ui.browse_iso.clicked.connect(self.browse_iso) + self.ui.comboBox.activated[str].connect(self.onComboChange) + # self.ui.create.clicked.connect(self.update_progress) + self.ui.create.clicked.connect(self.onCreateClick) + self.ui.slider_persistence.valueChanged.connect(self.update_slider_text) + self.ui.uninstall.clicked.connect(self.OnUninstallClick) + + # ISO Imager Tab + self.ui.pushButton.clicked.connect(self.on_Imager_Browse_iso_Click) + self.ui.comboBox_2.activated[str].connect(self.onImagerComboChange) + self.ui.pushbtn_imager_refreshusb.clicked.connect(self.onRefereshClick) + self.ui.imager_close.clicked.connect(self.on_close_Click) + self.ui.imager_write.clicked.connect(self.dd_write) + + # Syslinux Tab + self.ui.install_syslinux.clicked.connect(self.onInstall_syslinuxClick) + self.ui.edit_syslinux.clicked.connect(self.onedit_syslinux) + + # QEMU Tab + self.ui.browse_iso_qemu.clicked.connect(self.on_Qemu_Browse_iso_Click) + self.ui.boot_iso_qemu.clicked.connect(self.on_Qemu_Boot_iso_Click) + self.ui.boot_usb_qemu.clicked.connect(lambda: self.on_Qemu_Boot_usb_Click(str(self.ui.comboBox.currentText()))) + # self.ui.tabWidget.removeTab(3) + + # Update progressbar and status (Main ISO install) + self.progress_thread_install = GuiInstallProgress() + self.progress_thread_install.finished.connect(self.install_syslinux) + self.progress_thread_install.update.connect(self.ui.progressBar.setValue) + self.progress_thread_install.status.connect(self.ui.status.setText) + + # Update progressbar and status (Uninstall from previous install) + self.progress_thread_uninstall = GuiUninstallProgress() + self.progress_thread_uninstall.finished.connect(self.uninstall_sys_file_update) + self.progress_thread_uninstall.update.connect(self.ui.progressBar.setValue) + self.progress_thread_uninstall.status.connect(self.ui.status.setText) + + # Update progressbar and status (dd ISO) + self.progress_thread_dd = DD_Progress() + self.progress_thread_dd.update.connect(self.ui.imager_progressbar.setValue) + self.progress_thread_dd.finished.connect(self.dd_finished) + self.progress_thread_dd.status.connect(self.ui.imager_label_status.setText) + + self.add_device() + prepare_mbusb_host_dir() + + def add_device(self): + """ + Adds list of available USB devices to GUI combobox. + :return: + """ + self.ui.comboBox.clear() + self.ui.comboBox_2.clear() + if self.ui.checkBox_all_drives.isChecked(): + detected_device = usb.list(partition=1, fixed=True) + else: + detected_device = usb.list() + if bool(detected_device): + for device in detected_device: + self.ui.comboBox.addItem(str(device)) + if self.ui.comboBox.currentText(): + self.onComboChange() + + imager_detected_device = self.imager_list_usb(partition=0) + if bool(imager_detected_device): + for disk in imager_detected_device: + self.ui.comboBox_2.addItem(str(disk)) + self.onImagerComboChange() + + def onComboChange(self): + """ + Detects and updates GUI with populated USB device details. + :return: + """ + self.ui.listWidget.clear() + config.usb_disk = str(self.ui.comboBox.currentText()) + config.imager_usb_disk = str(self.ui.comboBox_2.currentText()) + if bool(config.usb_disk): + self.update_gui_oncombobox(config.usb_disk) + else: + log("No USB disk found...") + + def onRefereshClick(self): + """ + Calls function to detect USB devices. + :return: + """ + self.ui.comboBox.clear() + self.ui.comboBox_2.clear() + self.add_device() + + def update_gui_oncombobox(self, usb_disk): + self.usb_details = usb.details(usb_disk) + config.usb_mount = self.usb_details['mount_point'] + self.ui.usb_dev.setText("Drive :: " + usb_disk) + # self.label.setFont(QtGui.QFont("Times",weight=QtGui.QFont.Bold)) + if platform.system() == 'Windows': + self.ui.usb_vendor.setText("FileSystem :: " + self.usb_details['file_system']) + self.ui.usb_model.setText("Label :: " + self.usb_details['label']) + else: + self.ui.usb_vendor.setText("Vendor :: " + self.usb_details['vendor']) + self.ui.usb_model.setText("Model :: " + self.usb_details['model']) + self.ui.usb_size.setText("Total Size :: " + str(usb.bytes2human(self.usb_details['size_total']))) + self.ui.usb_mount.setText("Mount :: " + self.usb_details['mount_point']) + self.update_list_box(usb_disk) + + + def update_list_box(self, usb_disk): + """ + Updates listbox with installed distros on selected USB disk. + :param usb_mount: Selected USB disk from combobox. + :return: + """ + distro_list = install_distro_list() + #sys_cfg_file = os.path.join(str(usb_mount), "multibootusb", "syslinux.cfg") + if distro_list is not None: + self.ui.listWidget.clear() + for name in distro_list: + self.ui.listWidget.addItem(name) + else: + if config.usb_mount == 'No_Mount': + log("UBS disk is not mounted and can't update list widget...") + #QtWidgets.QMessageBox.information(self, 'No Install...', + # 'syslinux.cfg does not exist for updating list widget.') + + def browse_iso(self): + if str(self.ui.lineEdit.text()): + self.ui.lineEdit.clear() + config.iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', '', 'ISO Files (*.iso)')[0] + if config.iso_link: + if platform.system() == "Windows": + if "/" in config.iso_link: + config.iso_link = config.iso_link.strip().replace("/", "\\") + self.ui.lineEdit.insert(str(config.iso_link)) + if os.path.exists(config.iso_link): + clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned. + extract_cfg_file(config.iso_link) + config.distro = distro(iso_cfg_ext_dir(), config.iso_link) # Detect supported distro + if config.distro: + per_availability = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[0] + per_max_size = persistence.persistence_distro(config.distro, config.usb_disk, config.iso_link)[1] + if per_availability is not None: + self.ui.slider_persistence.setEnabled(True) + self.ui.slider_persistence.setTickInterval(10) + self.ui.slider_persistence.setSingleStep(10) + ui_per_max_size = per_max_size / 1024 / 1024 + # config.persistence = per_max_size + self.ui.slider_persistence.setMaximum(ui_per_max_size) + log('Persistence Max Size: ' + str(bytes2human(per_max_size))) + else: + log('Persistence is not available for ' + iso_name(config.iso_link)) + else: + log("File not selected...") + + def update_slider_text(self): + slide_value = self.ui.slider_persistence.value() * 1024 * 1024 + self.ui.label_persistence_value.setText(bytes2human(slide_value)) + config.persistence = slide_value + + def install_syslinux(self): + """ + Function to install syslinux on distro directory and on selected USB disks. + :return: + """ + self.ui.status.setText(str("Installing Syslinux...")) + syslinux_distro_dir(config.usb_disk, config.iso_link, config.distro) + syslinux_default(config.usb_disk) + update_distro_cfg_files(config.iso_link, config.usb_disk, config.distro, config.persistence) + self.update_list_box(config.usb_disk) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Finished...', iso_name(config.iso_link) + ' has been successfully installed.') + config.process_exist = None + + def onInstall_syslinuxClick(self): + """ + Function to install syslinux/extlinux on selected USB disk. + :return: + """ + if platform.system() == "Linux" or platform.system() == "Windows": + + if self.ui.install_sys_all.isChecked() or self.ui.install_sys_only.isChecked(): + log("Installing default syslinux on ", config.usb_disk) + ret = syslinux_default(config.usb_disk) + if ret is True: + if self.ui.install_sys_all.isChecked(): + log("Copying multibootusb directory to " + config.usb_mount) + for dirpath, dirnames, filenames in os.walk(resource_path(os.path.join("tools", "multibootusb"))): + for f in filenames: + log("Copying " + f) + shutil.copy(resource_path(os.path.join(dirpath, f)), os.path.join(self.usb.get_usb(config.usb_disk).mount, "multibootusb")) + QtWidgets.QMessageBox.information(self, 'Install Success...', + 'Syslinux installed successfully on ' + config.usb_disk) + elif ret is False: + QtWidgets.QMessageBox.information(self, 'Install error...', + 'Sorry. Syslinux failed to install on ' + config.usb_disk) + else: + QtWidgets.QMessageBox.information(self, 'No selection...', + 'Please select one of the option from above.') + + def onedit_syslinux(self): + """ + Function to edit main syslinux.cfg file. + :return: + """ + # Function to edit syslinux.cfg file on editors like gedit, notepad etc. + # Suggest me more editor which can be included in to this function. + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + log("Locating " + sys_cfg_file) + editor = '' + if not os.path.exists(sys_cfg_file): + log("syslinux.cfg file not found...") + QtWidgets.QMessageBox.information(self, 'File not found...', 'Sorry. Unable to locate syslinux.cfg file.\n' + 'You can only edit syslinux.cfg file generated by multibootusb.') + else: + if platform.system() == "Linux": + for e in config.editors_linux: + if subprocess.call('which ' + e, shell=True) == 0: + log("Editor found is " + e) + editor = e + break + elif platform.system() == "Windows": + for e in config.editors_win: + if not shutil.which(e) is None: + log("Editor found is " + e) + editor = e + break + if not editor: + QtWidgets.QMessageBox.information(self, 'Editor not found...', + 'Sorry. Installed editor is not supported by multibootusb\n' + 'Edit ' + sys_cfg_file + ' manually.\n') + else: + try: + subprocess.Popen(editor + " '" + sys_cfg_file + "'", shell=True).pid + except OSError: + QtWidgets.QMessageBox.warning(self, 'Error...', + 'Failed to open syslinux.cfg file.\n' + 'Edit syslinux.cfg file manually.\n') + + def OnUninstallClick(self): + """ + Triggers a function to uninstall a selected distro. + :return: + """ + if self.ui.listWidget.currentItem() is None: + log("Please select a distro from the list.") + QtWidgets.QMessageBox.information(self, 'No selection.', 'Please select a distro from the list.') + else: + config.uninstall_distro_dir_name = str(self.ui.listWidget.currentItem().text()).strip() + reply = QtWidgets.QMessageBox.question(self, "Review selection...", + "Are you sure to uninstall " + config.uninstall_distro_dir_name, + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + + if not os.path.exists(os.path.join(config.usb_mount, 'multibootusb', config.uninstall_distro_dir_name)): + log("Distro install directory not found. Just updating syslinux.cfg file.") + update_sys_cfg_file() + #self.uninstall.update_sys_cfg_file() + else: + self.progress_thread_uninstall.start() + + def uninstall_sys_file_update(self): + """ + Function to remove and update uninstall distro text. + :return: + """ + update_sys_cfg_file() + self.update_list_box(config.usb_mount) + if sys.platform.startswith("linux"): + self.ui.status.setText("Sync is in progress...") + os.system('sync') + self.ui.status.clear() + QtWidgets.QMessageBox.information(self, 'Uninstall Complete...', + config.uninstall_distro_dir_name + ' has been successfully removed.') + + def onCreateClick(self): + """ + Main function to create bootable USB disk. + :param usb_disk: ComboBox text as detected USB disk. + :param iso_link: LineEdit text as selected ISO link. + :return: + """ + if not config.usb_disk: + log("No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.") + QtWidgets.QMessageBox.information(self, "No Device...", + "No USB device found.\n\nInsert USB and use Refresh USB button to detect USB.") + elif not config.iso_link: + log("No ISO found.\n\nPlease use step 2 to choose an ISO.") + QtWidgets.QMessageBox.information(self, "No ISO...", "No ISO found.\n\nPlease use step 2 to choose an ISO.") + elif usb.details(config.usb_disk)['mount_point'] == 'No_Mount': + log("USB disk is not mounted.\nPlease mount USB disk and press refresh USB button.") + QtWidgets.QMessageBox.information(self, "No Mount...", "USB disk is not mounted.\n" + "Please mount USB disk and press refresh USB button.") + else: + # clean_iso_cfg_ext_dir(os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")) # Need to be cleaned. + # extract_cfg_file(config.iso_link) # Extract files from ISO + # config.distro = distro(iso_cfg_ext_dir(), config.iso_link) # Detect supported distro + usb_details = usb.details(config.usb_disk) + log("USB Disk is " + config.usb_disk) + log("USB Label is " + config.usb_label) + log("USB UUID is " + config.usb_uuid) + log("USB Mount path is " + config.usb_mount) + log("Total size of the disk is " + str(usb.bytes2human(usb_details['size_total']))) + log("Total used size is " + str(usb.bytes2human(usb_details['size_used']))) + log("Total size left on the disk is " + str(usb.bytes2human(usb_details['size_free']))) + log("FileSystem is " + usb_details['file_system']) + log("Vendor is " + usb_details['vendor']) + log("Model is " + usb_details['model']) + log("Name of the ISO file is " + iso_name(config.iso_link)) + + if os.path.exists(config.iso_link): + self.ui.lineEdit.clear() + if config.distro: + log("Distro type detected is " + config.distro) + copy_mbusb_dir_usb(config.usb_disk) + if not os.path.exists(os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link))): + config.persistence = self.ui.slider_persistence.value() * 1024 * 1024 + install_size = iso_size(config.iso_link) + config.persistence + log("Persistence choosen is " + str(config.persistence) + " MB") + if install_size >= disk_usage(config.usb_mount).free: + QtWidgets.QMessageBox.information(self, "No Space.", "No space available on " + + config.usb_disk) + else: + reply = QtWidgets.QMessageBox.question(self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.usb_disk + + 'USB mount point:: %s\n' % config.usb_mount + + 'Selected distro:: %s\n\n' % iso_name(config.iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.ui.slider_persistence.setEnabled(False) + config.process_exist = True + self.progress_thread_install.start() + + else: + QtWidgets.QMessageBox.information(self, 'Already Exist...', + os.path.basename(config.iso_link) + ' is already installed.') + else: + QtWidgets.QMessageBox.information(self, 'No support...', + 'Sorry.\n' + os.path.basename(config.iso_link) + + ' is not supported at the moment.\n' + 'Please email this issue to feedback.multibootusb@gmail.com') + + # Added to refresh usb disk remaining size after distro installation + # self.update_gui_usb_info() + + def dd_finished(self): + """ + Re-enable the blocked widgets for newer use. + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + self.ui.imager_bootable.setText("Bootable ISO :: ") + self.ui.imager_iso_size.setText("ISO Size :: ") + config.process_exist = None + QtWidgets.QMessageBox.information(self, 'Finished...', 'ISO has been written to USB disk.\nPlease reboot your ' + 'system to boot from USB.') + + def dd_start(self): + """ + Function to block the widgets under ISO Imager tab... + :return: + """ + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.lineEdit_3.clear() + self.ui.pushButton.setEnabled(False) + self.ui.comboBox_2.setEnabled(False) + self.ui.pushbtn_imager_refreshusb.setEnabled(False) + status_text = ("Writing " + os.path.basename(config.imager_iso_link) + "" + " to " + "" + + config.imager_usb_disk_selected + "") + self.ui.imager_label_status.setText(status_text) + + def dd_quit(self): + self.ui.imager_progressbar.setValue(0) + self.ui.imager_label_status.clear() + self.ui.comboBox_2.setEnabled(True) + self.ui.pushButton.setEnabled(True) + QtWidgets.QMessageBox.information(self, 'Failed!', 'Writing ISO failed.') + + def dd_write(self): + if not config.imager_usb_disk: + QtWidgets.QMessageBox.information(self, 'No USB...', 'Please Insert USB disk and rerun multibootusb.') + elif not config.imager_iso_link: + QtWidgets.QMessageBox.information(self, 'No ISO...', 'Please select an ISO.') + else: + usb_disk_size = int(self.imager_usb_detail(config.imager_usb_disk, partition=0).total_size) + self.iso_size = os.path.getsize(config.imager_iso_link) + if self.iso_size >= usb_disk_size: + QtWidgets.QMessageBox.information(self, "No Space.", os.path.basename(config.imager_iso_link) + + " size is larger than the size of " + config.imager_usb_disk) + else: + reply = QtWidgets.QMessageBox.question \ + (self, 'Review selection...', + 'Selected USB disk:: %s\n' % config.imager_usb_disk + + 'Selected distro:: %s\n\n' % os.path.basename(config.imager_iso_link) + + 'Would you like to proceed for installation?', + QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + + if reply == QtWidgets.QMessageBox.Yes: + self.dd_start() + config.process_exist = True + self.progress_thread_dd.start() + + def on_close_Click(self): + """ + Closes main GUI. + :return: + """ + self.close() + + def closeEvent(self, event): + """ + To capture the main close event. + :param event: Close event. + :return: + """ + if config.process_exist == None: + event.accept() + else: + reply = QtWidgets.QMessageBox.question(self, 'Exit MultiBootUSB...', + "A process is still running.\n" + "Do you really want to quit multibootusb?", QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + log("Closing multibootusb...") + event.accept() + sys.exit(0) + else: + log("Close event cancelled.") + event.ignore() + + +class GuiInstallProgress(QtCore.QThread): + """ + Update GUI thread during install. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + def __del__(self): + self.wait() + + def run(self): + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + self.thread = GenericThread(install_progress) + status_text = "" + self.thread.start() + while self.thread.isRunning(): + if config.status_text.strip(): + config.status_text = config.status_text.replace(install_dir + "/", "Extracting ") + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + self.status.emit("Installing boot loader...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + log("Distro extraction completed...") + + return + + +class GuiUninstallProgress(QtCore.QThread): + """ + Update GUI thread during uninstall. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + self.thread = GenericThread(uninstall_progress) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + self.update.emit(config.percentage) + self.status.emit(config.status_text) + if not self.thread.isFinished() and config.percentage == 100: + config.status_text = "Please wait..." + self.update.emit(100) + self.update.emit(0) + config.percentage = 0 + self.status.emit("Updating syslinux.cfg file...") + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + log("Distro uninstall is complete...") + + return + + +class DD_Progress(QtCore.QThread): + """ + Update GUI progress bar without blocking rest of GUI element when dd process is in progress. + """ + update = QtCore.pyqtSignal(int) + status = QtCore.pyqtSignal(str) + finished = QtCore.pyqtSignal() + + def __init__(self): + QtCore.QThread.__init__(self) + + if platform.system() == 'Linux': + self.thread = GenericThread(dd_linux) + elif platform.system() == 'Windows': + self.thread = GenericThread(dd_win) + + def __del__(self): + self.wait() + + def run(self): + self.thread.start() + while self.thread.isRunning(): + if config.imager_percentage: + self.update.emit(config.imager_percentage) + if not self.thread.isFinished() and config.percentage == 100: + config.imager_status_text = "" + self.status.emit("Please wait...") + + self.update.emit(100) + self.update.emit(0) + + if self.thread.isFinished(): + config.status_text = "" + self.finished.emit() + + return + + +class GenericThread(QtCore.QThread): + + def __init__(self, function, *args, **kwargs): + QtCore.QThread.__init__(self) + self.function = function + self.args = args + self.kwargs = kwargs + + def __del__(self): + self.wait() + + def run(self): + self.function(*self.args, **self.kwargs) + return + + +def show_admin_info(): + """ + Show simple information box reminding user to run the software with admin/root privilege. + Only required under Linux as the windows executable always will start with admin privilege. + :return: + """ + msg = QtWidgets.QMessageBox() + msg.setIcon(QtWidgets.QMessageBox.Information) + msg.setText('Admin privilege is required to run multibootusb.\n If you are running from source try ' + '\'sudo python3 ./multibootusb\'\n or you can try \'multibootusb-pkexec\' (post install)') + msg.exec_() + + +def main_gui(): + app = QtWidgets.QApplication(sys.argv) + window = AppGui() + ui = Ui_Dialog() + window.show() + window.setWindowTitle("MultiBootUSB - " + mbusb_version()) + window.setWindowIcon(QtGui.QIcon(resource_path(os.path.join("data", "tools", "multibootusb.png")))) + if platform.system() == 'Linux': + if os.getuid() != 0: + show_admin_info() + sys.exit(app.exec_()) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/persistence.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/persistence.py new file mode 100644 index 0000000..66925e8 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/persistence.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: persistence.py +# Purpose: Module to deal with persistence of a selected distro. +# 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 sys +import os +import platform +import tarfile +import subprocess +from . import usb +from . import iso +from . import gen +from . import config + + +def persistence_distro(distro, usb_disk, iso_link): + """ + Function to detect if distro can have persistence option. + :param distro: Detected distro name. + :return: Distro name as string or None otherwise. + """ + iso_size = iso.iso_size(iso_link) + fat_max_size = (4096 * 1024 * 1024) + usb_details = usb.details(usb_disk) + usb_sf = usb_details['file_system'] + usb_free_size = usb_details['size_free'] + config.usb_uuid = usb_details['uuid'] + config.usb_label = usb_details['label'] + if usb_sf == 'vfat' or 'FAT32': + if usb_free_size > fat_max_size: + _max_size = fat_max_size + else: + _max_size = usb_free_size + else: + _max_size = usb_free_size + if distro == "ubuntu": + gen.log("Persistence option is available.") + return "ubuntu", _max_size + # FIXME to get debian persistence workable... + # Able to add successfully but unable to keep persistence data. + elif distro == "debian" or distro == "debian-install": + gen.log("Persistence option is available.") + return "debian", _max_size + elif distro == "fedora": + gen.log("Persistence option is available.") + return "fedora", _max_size + else: + return None, None + + +def create_persistence(): + if config.distro == "ubuntu": + fs_name = 'casper-rw' + elif config.distro == 'debian' or config.distro == "debian-install": + fs_name = 'live-rw' + elif config.distro == 'fedora': + fs_name = 'overlay-' + config.usb_label + '-' + config.usb_uuid + + persistence = config.persistence / 1024 / 1024 + + if platform.system() == 'Linux': + mkfs = 'mkfs.ext3' + dd = 'dd' + persistence_mkfs_cmd = mkfs + ' -F ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), + fs_name) + elif platform.system() == 'Windows': + mkfs = gen.quote(gen.resource_path(os.path.join("data", "tools", "mkfs", "mke2fs.exe"))) + dd = gen.quote(gen.resource_path(os.path.join("data", "tools", "dd", "dd.exe"))) + persistence_mkfs_cmd = 'echo y|' + mkfs + ' -b 1024 -L ' + fs_name + ' ' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) + + if config.distro == 'fedora': + persistence_dd_cmd = dd + ' if=/dev/zero ' \ + 'of=' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), 'LiveOS', fs_name) + \ + ' bs=1M count=' + str(int(persistence)) + else: + persistence_dd_cmd = dd + ' if=/dev/zero of=' + os.path.join(config.usb_mount, 'multibootusb', + iso.iso_basename(config.iso_link), fs_name) +\ + ' bs=1M count=' + str(int(persistence)) + + gen.log('Executing ==>' + persistence_dd_cmd) + config.status_text = 'Creating persistence file...' + + if subprocess.call(persistence_dd_cmd, shell=True) == 0: + gen.log("\nSuccessfully created persistence file...\n") + + if not config.distro == 'fedora': + gen.log('Applying filesystem to persistence file...') + gen.log('Executing ==> ' + persistence_mkfs_cmd) + config.status_text = 'Applying filesystem to persistence file...' + if subprocess.call(persistence_mkfs_cmd, shell=True) == 0: + gen.log("\nSuccessfully applied filesystem...\n") + + +def extract_file(file_path, install_dir): + """ + Function to extract persistence files to distro install directory. + :param file_path: Path to persistence file. + :param install_dir: Path to distro install directory. + :return: + """ + tar = tarfile.open(file_path, "r:bz2") + tar.extractall(install_dir) + tar.close() + diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/__init__.py new file mode 100644 index 0000000..fbab744 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/__init__.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Text progress bar library for Python. + +A text progress bar is typically used to display the progress of a long +running operation, providing a visual cue that processing is underway. + +The ProgressBar class manages the current progress, and the format of the line +is given by a number of widgets. A widget is an object that may display +differently depending on the state of the progress bar. There are three types +of widgets: + - a string, which always shows itself + + - a ProgressBarWidget, which may return a different value every time its + update method is called + + - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it + expands to fill the remaining width of the line. + +The progressbar module is very easy to use, yet very powerful. It will also +automatically enable features like auto-resizing when the system supports it. +""" + +__author__ = 'Nilton Volpato' +__author_email__ = 'first-name dot last-name @ gmail.com' +__date__ = '2011-05-14' +__version__ = '2.3' + +from .compat import * +from .widgets import * +from .progressbar import * diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/compat.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/compat.py new file mode 100644 index 0000000..a39f4a1 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Compatibility methods and classes for the progressbar module.""" + + +# Python 3.x (and backports) use a modified iterator syntax +# This will allow 2.x to behave with 3.x iterators +try: + next +except NameError: + def next(iter): + try: + # Try new style iterators + return iter.__next__() + except AttributeError: + # Fallback in case of a "native" iterator + return iter.next() + + +# Python < 2.5 does not have "any" +try: + any +except NameError: + def any(iterator): + for item in iterator: + if item: return True + return False diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/progressbar.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/progressbar.py new file mode 100644 index 0000000..7ab79b6 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/progressbar.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Main ProgressBar class.""" + +from __future__ import division + +import math +import os +import signal +import sys +import time + +try: + from fcntl import ioctl + from array import array + import termios +except ImportError: + pass + +from .compat import * # for: any, next +from . import widgets + + +class UnknownLength: pass + + +class ProgressBar(object): + """The ProgressBar class which updates and prints the bar. + + A common way of using it is like: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + + You can also use a ProgressBar as an iterator: + >>> progress = ProgressBar() + >>> for i in progress(some_iterable): + ... # do something + ... + + Since the progress bar is incredibly customizable you can specify + different widgets of any type in any order. You can even write your own + widgets! However, since there are already a good number of widgets you + should probably play around with them before moving on to create your own + widgets. + + The term_width parameter represents the current terminal width. If the + parameter is set to an integer then the progress bar will use that, + otherwise it will attempt to determine the terminal width falling back to + 80 columns if the width cannot be determined. + + When implementing a widget's update method you are passed a reference to + the current progress bar. As a result, you have access to the + ProgressBar's methods and attributes. Although there is nothing preventing + you from changing the ProgressBar you should treat it as read only. + + Useful methods and attributes include (Public API): + - currval: current progress (0 <= currval <= maxval) + - maxval: maximum (and final) value + - finished: True if the bar has finished (reached 100%) + - start_time: the time when start() method of ProgressBar was called + - seconds_elapsed: seconds elapsed since start_time and last call to + update + - percentage(): progress in percent [0..100] + """ + + __slots__ = ('currval', 'fd', 'finished', 'last_update_time', + 'left_justify', 'maxval', 'next_update', 'num_intervals', + 'poll', 'seconds_elapsed', 'signal_set', 'start_time', + 'term_width', 'update_interval', 'widgets', '_time_sensitive', + '__iterable') + + _DEFAULT_MAXVAL = 100 + _DEFAULT_TERMSIZE = 80 + _DEFAULT_WIDGETS = [widgets.Percentage(), ' ', widgets.Bar()] + + def __init__(self, maxval=None, widgets=None, term_width=None, poll=1, + left_justify=True, fd=sys.stderr): + """Initializes a progress bar with sane defaults.""" + + # Don't share a reference with any other progress bars + if widgets is None: + widgets = list(self._DEFAULT_WIDGETS) + + self.maxval = maxval + self.widgets = widgets + self.fd = fd + self.left_justify = left_justify + + self.signal_set = False + if term_width is not None: + self.term_width = term_width + else: + try: + self._handle_resize() + signal.signal(signal.SIGWINCH, self._handle_resize) + self.signal_set = True + except (SystemExit, KeyboardInterrupt): raise + except: + self.term_width = self._env_size() + + self.__iterable = None + self._update_widgets() + self.currval = 0 + self.finished = False + self.last_update_time = None + self.poll = poll + self.seconds_elapsed = 0 + self.start_time = None + self.update_interval = 1 + self.next_update = 0 + + + def __call__(self, iterable): + """Use a ProgressBar to iterate through an iterable.""" + + try: + self.maxval = len(iterable) + except: + if self.maxval is None: + self.maxval = UnknownLength + + self.__iterable = iter(iterable) + return self + + + def __iter__(self): + return self + + + def __next__(self): + try: + value = next(self.__iterable) + if self.start_time is None: + self.start() + else: + self.update(self.currval + 1) + return value + except StopIteration: + if self.start_time is None: + self.start() + self.finish() + raise + + + # Create an alias so that Python 2.x won't complain about not being + # an iterator. + next = __next__ + + + def _env_size(self): + """Tries to find the term_width from the environment.""" + + return int(os.environ.get('COLUMNS', self._DEFAULT_TERMSIZE)) - 1 + + + def _handle_resize(self, signum=None, frame=None): + """Tries to catch resize signals sent from the terminal.""" + + h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] + self.term_width = w + + + def percentage(self): + """Returns the progress as a percentage.""" + if self.currval >= self.maxval: + return 100.0 + return (self.currval * 100.0 / self.maxval) if self.maxval else 100.00 + + percent = property(percentage) + + + def _format_widgets(self): + result = [] + expanding = [] + width = self.term_width + + for index, widget in enumerate(self.widgets): + if isinstance(widget, widgets.WidgetHFill): + result.append(widget) + expanding.insert(0, index) + else: + widget = widgets.format_updatable(widget, self) + result.append(widget) + width -= len(widget) + + count = len(expanding) + while count: + portion = max(int(math.ceil(width * 1. / count)), 0) + index = expanding.pop() + count -= 1 + + widget = result[index].update(self, portion) + width -= len(widget) + result[index] = widget + + return result + + + def _format_line(self): + """Joins the widgets and justifies the line.""" + + widgets = ''.join(self._format_widgets()) + + if self.left_justify: return widgets.ljust(self.term_width) + else: return widgets.rjust(self.term_width) + + + def _need_update(self): + """Returns whether the ProgressBar should redraw the line.""" + if self.currval >= self.next_update or self.finished: return True + + delta = time.time() - self.last_update_time + return self._time_sensitive and delta > self.poll + + + def _update_widgets(self): + """Checks all widgets for the time sensitive bit.""" + + self._time_sensitive = any(getattr(w, 'TIME_SENSITIVE', False) + for w in self.widgets) + + + def update(self, value=None): + """Updates the ProgressBar to a new value.""" + + if value is not None and value is not UnknownLength: + if (self.maxval is not UnknownLength + and not 0 <= value <= self.maxval): + + raise ValueError('Value out of range') + + self.currval = value + + + if not self._need_update(): return + if self.start_time is None: + raise RuntimeError('You must call "start" before calling "update"') + + now = time.time() + self.seconds_elapsed = now - self.start_time + self.next_update = self.currval + self.update_interval + self.fd.write(self._format_line() + '\r') + self.fd.flush() + self.last_update_time = now + + + def start(self): + """Starts measuring time, and prints the bar at 0%. + + It returns self so you can use it like this: + >>> pbar = ProgressBar().start() + >>> for i in range(100): + ... # do something + ... pbar.update(i+1) + ... + >>> pbar.finish() + """ + + if self.maxval is None: + self.maxval = self._DEFAULT_MAXVAL + + self.num_intervals = max(100, self.term_width) + self.next_update = 0 + + if self.maxval is not UnknownLength: + if self.maxval < 0: raise ValueError('Value out of range') + self.update_interval = self.maxval / self.num_intervals + + + self.start_time = self.last_update_time = time.time() + self.update(0) + + return self + + + def finish(self): + """Puts the ProgressBar bar in the finished state.""" + + if self.finished: + return + self.finished = True + self.update(self.maxval) + self.fd.write('\n') + if self.signal_set: + signal.signal(signal.SIGWINCH, signal.SIG_DFL) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/widgets.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/widgets.py new file mode 100644 index 0000000..6434ad5 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/progressbar/widgets.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# progressbar - Text progress bar library for Python. +# Copyright (c) 2005 Nilton Volpato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Default ProgressBar widgets.""" + +from __future__ import division + +import datetime +import math + +try: + from abc import ABCMeta, abstractmethod +except ImportError: + AbstractWidget = object + abstractmethod = lambda fn: fn +else: + AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) + + +def format_updatable(updatable, pbar): + if hasattr(updatable, 'update'): return updatable.update(pbar) + else: return updatable + + +class Widget(AbstractWidget): + """The base class for all widgets. + + The ProgressBar will call the widget's update value when the widget should + be updated. The widget's size may change between calls, but the widget may + display incorrectly if the size changes drastically and repeatedly. + + The boolean TIME_SENSITIVE informs the ProgressBar that it should be + updated more often because it is time sensitive. + """ + + TIME_SENSITIVE = False + __slots__ = () + + @abstractmethod + def update(self, pbar): + """Updates the widget. + + pbar - a reference to the calling ProgressBar + """ + + +class WidgetHFill(Widget): + """The base class for all variable width widgets. + + This widget is much like the \\hfill command in TeX, it will expand to + fill the line. You can use more than one in the same line, and they will + all have the same width, and together will fill the line. + """ + + @abstractmethod + def update(self, pbar, width): + """Updates the widget providing the total width the widget must fill. + + pbar - a reference to the calling ProgressBar + width - The total width the widget must fill + """ + + +class Timer(Widget): + """Widget which displays the elapsed seconds.""" + + __slots__ = ('format_string',) + TIME_SENSITIVE = True + + def __init__(self, format='Elapsed Time: %s'): + self.format_string = format + + @staticmethod + def format_time(seconds): + """Formats time as the string "HH:MM:SS".""" + + return str(datetime.timedelta(seconds=int(seconds))) + + + def update(self, pbar): + """Updates the widget to show the elapsed time.""" + + return self.format_string % self.format_time(pbar.seconds_elapsed) + + +class ETA(Timer): + """Widget which attempts to estimate the time of arrival.""" + + TIME_SENSITIVE = True + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + eta = elapsed * pbar.maxval / pbar.currval - elapsed + return 'ETA: %s' % self.format_time(eta) + + +class AdaptiveETA(Timer): + """Widget which attempts to estimate the time of arrival. + + Uses a weighted average of two estimates: + 1) ETA based on the total progress and time elapsed so far + 2) ETA based on the progress as per the last 10 update reports + + The weight depends on the current progress so that to begin with the + total progress is used and at the end only the most recent progress is + used. + """ + + TIME_SENSITIVE = True + NUM_SAMPLES = 10 + + def _update_samples(self, currval, elapsed): + sample = (currval, elapsed) + if not hasattr(self, 'samples'): + self.samples = [sample] * (self.NUM_SAMPLES + 1) + else: + self.samples.append(sample) + return self.samples.pop(0) + + def _eta(self, maxval, currval, elapsed): + return elapsed * maxval / float(currval) - elapsed + + def update(self, pbar): + """Updates the widget to show the ETA or total time when finished.""" + if pbar.currval == 0: + return 'ETA: --:--:--' + elif pbar.finished: + return 'Time: %s' % self.format_time(pbar.seconds_elapsed) + else: + elapsed = pbar.seconds_elapsed + currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) + eta = self._eta(pbar.maxval, pbar.currval, elapsed) + if pbar.currval > currval1: + etasamp = self._eta(pbar.maxval - currval1, + pbar.currval - currval1, + elapsed - elapsed1) + weight = (pbar.currval / float(pbar.maxval)) ** 0.5 + eta = (1 - weight) * eta + weight * etasamp + return 'ETA: %s' % self.format_time(eta) + + +class FileTransferSpeed(Widget): + """Widget for showing the transfer speed (useful for file transfers).""" + + FORMAT = '%6.2f %s%s/s' + PREFIXES = ' kMGTPEZY' + __slots__ = ('unit',) + + def __init__(self, unit='B'): + self.unit = unit + + def update(self, pbar): + """Updates the widget with the current SI prefixed speed.""" + + if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 + scaled = power = 0 + else: + speed = pbar.currval / pbar.seconds_elapsed + power = int(math.log(speed, 1000)) + scaled = speed / 1000.**power + + return self.FORMAT % (scaled, self.PREFIXES[power], self.unit) + + +class AnimatedMarker(Widget): + """An animated marker for the progress bar which defaults to appear as if + it were rotating. + """ + + __slots__ = ('markers', 'curmark') + + def __init__(self, markers='|/-\\'): + self.markers = markers + self.curmark = -1 + + def update(self, pbar): + """Updates the widget to show the next marker or the first marker when + finished""" + + if pbar.finished: return self.markers[0] + + self.curmark = (self.curmark + 1) % len(self.markers) + return self.markers[self.curmark] + +# Alias for backwards compatibility +RotatingMarker = AnimatedMarker + + +class Counter(Widget): + """Displays the current count.""" + + __slots__ = ('format_string',) + + def __init__(self, format='%d'): + self.format_string = format + + def update(self, pbar): + return self.format_string % pbar.currval + + +class Percentage(Widget): + """Displays the current percentage as a number with a percent sign.""" + + def update(self, pbar): + return '%3d%%' % pbar.percentage() + + +class FormatLabel(Timer): + """Displays a formatted label.""" + + mapping = { + 'elapsed': ('seconds_elapsed', Timer.format_time), + 'finished': ('finished', None), + 'last_update': ('last_update_time', None), + 'max': ('maxval', None), + 'seconds': ('seconds_elapsed', None), + 'start': ('start_time', None), + 'value': ('currval', None) + } + + __slots__ = ('format_string',) + def __init__(self, format): + self.format_string = format + + def update(self, pbar): + context = {} + for name, (key, transform) in self.mapping.items(): + try: + value = getattr(pbar, key) + + if transform is None: + context[name] = value + else: + context[name] = transform(value) + except: pass + + return self.format_string % context + + +class SimpleProgress(Widget): + """Returns progress as a count of the total (e.g.: "5 of 47").""" + + __slots__ = ('sep',) + + def __init__(self, sep=' of '): + self.sep = sep + + def update(self, pbar): + return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) + + +class Bar(WidgetHFill): + """A progress bar which stretches to fill the line.""" + + __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=True): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marked, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + # Marked must *always* have length of 1 + if pbar.maxval: + marked *= int(pbar.currval / pbar.maxval * width) + else: + marked = '' + + if self.fill_left: + return '%s%s%s' % (left, marked.ljust(width, self.fill), right) + else: + return '%s%s%s' % (left, marked.rjust(width, self.fill), right) + + +class ReverseBar(Bar): + """A bar which has a marker which bounces from side to side.""" + + def __init__(self, marker='#', left='|', right='|', fill=' ', + fill_left=False): + """Creates a customizable progress bar. + + marker - string or updatable object to use as a marker + left - string or updatable object to use as a left border + right - string or updatable object to use as a right border + fill - character to use for the empty part of the progress bar + fill_left - whether to fill from the left or the right + """ + self.marker = marker + self.left = left + self.right = right + self.fill = fill + self.fill_left = fill_left + + +class BouncingBar(Bar): + def update(self, pbar, width): + """Updates the progress bar and its subcomponents.""" + + left, marker, right = (format_updatable(i, pbar) for i in + (self.left, self.marker, self.right)) + + width -= len(left) + len(right) + + if pbar.finished: return '%s%s%s' % (left, width * marker, right) + + position = int(pbar.currval % (width * 2 - 1)) + if position > width: position = width * 2 - position + lpad = self.fill * (position - 1) + rpad = self.fill * (width - len(marker) - len(lpad)) + + # Swap if we want to bounce the other way + if not self.fill_left: rpad, lpad = lpad, rpad + + return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/__init__.py new file mode 100644 index 0000000..e757b74 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/__init__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev + ====== + + A binding to libudev. + + The :class:`Context` provides the connection to the udev device database + and enumerates devices. Individual devices are represented by the + :class:`Device` class. + + Device monitoring is provided by :class:`Monitor` and + :class:`MonitorObserver`. With :mod:`pyudev.pyqt4`, :mod:`pyudev.pyside`, + :mod:`pyudev.glib` and :mod:`pyudev.wx` device monitoring can be integrated + into the event loop of various GUI toolkits. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ._errors import DeviceNotFoundAtPathError +from ._errors import DeviceNotFoundByFileError +from ._errors import DeviceNotFoundByNameError +from ._errors import DeviceNotFoundByNumberError +from ._errors import DeviceNotFoundError +from ._errors import DeviceNotFoundInEnvironmentError + +from .device import Attributes +from .device import Device +from .device import Devices +from .device import Tags + +from .discover import DeviceFileHypothesis +from .discover import DeviceNameHypothesis +from .discover import DeviceNumberHypothesis +from .discover import DevicePathHypothesis +from .discover import Discovery + +from .core import Context +from .core import Enumerator + +from .monitor import Monitor +from .monitor import MonitorObserver + +from .version import __version__ +from .version import __version_info__ + +from ._util import udev_version diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_compat.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_compat.py new file mode 100644 index 0000000..f21eb98 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_compat.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._compat + ============== + + Compatibility for Python versions, that lack certain functions. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from subprocess import Popen, CalledProcessError, PIPE + + +def check_output(command): + """ + Compatibility with :func:`subprocess.check_output` from Python 2.7 and + upwards. + """ + proc = Popen(command, stdout=PIPE) + output = proc.communicate()[0] + if proc.returncode != 0: + raise CalledProcessError(proc.returncode, command) + return output diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py new file mode 100644 index 0000000..cb5ed88 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib + ================= + + Wrappers for libraries. + + .. moduleauthor:: mulhern +""" + +from . import libc +from . import libudev diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py new file mode 100644 index 0000000..f4de520 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/_errorcheckers.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib._errorcheckers + ================================ + + Error checkers for ctypes wrappers. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + + +import os +import errno +from ctypes import get_errno + + +ERRNO_EXCEPTIONS = { + errno.ENOMEM: MemoryError, + errno.EOVERFLOW: OverflowError, + errno.EINVAL: ValueError +} + + +def exception_from_errno(errnum): + """Create an exception from ``errnum``. + + ``errnum`` is an integral error number. + + Return an exception object appropriate to ``errnum``. + + """ + exception = ERRNO_EXCEPTIONS.get(errnum) + errorstr = os.strerror(errnum) + if exception is not None: + return exception(errorstr) + else: + return EnvironmentError(errnum, errorstr) + + +def check_negative_errorcode(result, func, *args): + """Error checker for funtions, which return negative error codes. + + If ``result`` is smaller than ``0``, it is interpreted as negative error + code, and an appropriate exception is raised: + + - ``-ENOMEM`` raises a :exc:`~exceptions.MemoryError` + - ``-EOVERFLOW`` raises a :exc:`~exceptions.OverflowError` + - all other error codes raise :exc:`~exceptions.EnvironmentError` + + If result is greater or equal to ``0``, it is returned unchanged. + + """ + if result < 0: + # udev returns the *negative* errno code at this point + errnum = -result + raise exception_from_errno(errnum) + else: + return result + + +def check_errno_on_nonzero_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is not ``0``, an exception according to this errno is raised. + Otherwise nothing happens. + + """ + if result != 0: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result + + +def check_errno_on_null_pointer_return(result, func, *args): + """Error checker to check the system ``errno`` as returned by + :func:`ctypes.get_errno()`. + + If ``result`` is a null pointer, an exception according to this errno is + raised. Otherwise nothing happens. + + """ + # pylint: disable=invalid-name + if not result: + errnum = get_errno() + if errnum != 0: + raise exception_from_errno(errnum) + return result diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py new file mode 100644 index 0000000..112c73b --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libc.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.libc + ====================== + + Wrappers for libc. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_int + +from ._errorcheckers import check_errno_on_nonzero_return + + +fd_pair = c_int * 2 + + +SIGNATURES = dict( + pipe2=([fd_pair, c_int], c_int), +) + +ERROR_CHECKERS = dict( + pipe2=check_errno_on_nonzero_return, +) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py new file mode 100644 index 0000000..901b124 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/libudev.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._ctypeslib.libudev + ========================= + + Wrapper types for libudev. Use ``libudev`` attribute to access libudev + functions. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_uint +from ctypes import c_ulonglong +from ctypes import CDLL +from ctypes import Structure +from ctypes import POINTER + +from ctypes.util import find_library + +from ._errorcheckers import check_errno_on_nonzero_return +from ._errorcheckers import check_errno_on_null_pointer_return +from ._errorcheckers import check_negative_errorcode + + +class udev(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_p = POINTER(udev) # pylint: disable=invalid-name + + +class udev_enumerate(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_enumerate`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_enumerate_p = POINTER(udev_enumerate) # pylint: disable=invalid-name + + +class udev_list_entry(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_list_entry`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_list_entry_p = POINTER(udev_list_entry) # pylint: disable=invalid-name + + +class udev_device(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_device_p = POINTER(udev_device) # pylint: disable=invalid-name + + +class udev_monitor(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_device`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_monitor_p = POINTER(udev_monitor) # pylint: disable=invalid-name + +class udev_hwdb(Structure): # pylint: disable=invalid-name + """ + Dummy for ``udev_hwdb`` structure. + """ + # pylint: disable=too-few-public-methods + pass + +udev_hwdb_p = POINTER(udev_hwdb) # pylint: disable=invalid-name + + +dev_t = c_ulonglong # pylint: disable=invalid-name + + +SIGNATURES = dict( + # context + udev_new=([], udev_p), + udev_unref=([udev_p], None), + udev_ref=([udev_p], udev_p), + udev_get_sys_path=([udev_p], c_char_p), + udev_get_dev_path=([udev_p], c_char_p), + udev_get_run_path=([udev_p], c_char_p), + udev_get_log_priority=([udev_p], c_int), + udev_set_log_priority=([udev_p, c_int], None), + udev_enumerate_new=([udev_p], udev_enumerate_p), + udev_enumerate_ref=([udev_enumerate_p], udev_enumerate_p), + udev_enumerate_unref=([udev_enumerate_p], None), + udev_enumerate_add_match_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_nomatch_subsystem=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_property=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_nomatch_sysattr=( + [udev_enumerate_p, c_char_p, c_char_p], + c_int + ), + udev_enumerate_add_match_tag=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_sysname=([udev_enumerate_p, c_char_p], c_int), + udev_enumerate_add_match_parent=([udev_enumerate_p, udev_device_p], c_int), + udev_enumerate_add_match_is_initialized=([udev_enumerate_p], c_int), + udev_enumerate_scan_devices=([udev_enumerate_p], c_int), + udev_enumerate_get_list_entry=([udev_enumerate_p], udev_list_entry_p), + # list entries + udev_list_entry_get_next=([udev_list_entry_p], udev_list_entry_p), + udev_list_entry_get_name=([udev_list_entry_p], c_char_p), + udev_list_entry_get_value=([udev_list_entry_p], c_char_p), + # devices + udev_device_ref=([udev_device_p], udev_device_p), + udev_device_unref=([udev_device_p], None), + udev_device_new_from_syspath=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_subsystem_sysname=( + [udev_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_new_from_devnum=([udev_p, c_char, dev_t], udev_device_p), + udev_device_new_from_device_id=([udev_p, c_char_p], udev_device_p), + udev_device_new_from_environment=([udev_p], udev_device_p), + udev_device_get_parent=([udev_device_p], udev_device_p), + udev_device_get_parent_with_subsystem_devtype=( + [udev_device_p, c_char_p, c_char_p], + udev_device_p + ), + udev_device_get_devpath=([udev_device_p], c_char_p), + udev_device_get_subsystem=([udev_device_p], c_char_p), + udev_device_get_syspath=([udev_device_p], c_char_p), + udev_device_get_sysnum=([udev_device_p], c_char_p), + udev_device_get_sysname=([udev_device_p], c_char_p), + udev_device_get_driver=([udev_device_p], c_char_p), + udev_device_get_devtype=([udev_device_p], c_char_p), + udev_device_get_devnode=([udev_device_p], c_char_p), + udev_device_get_property_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_sysattr_value=([udev_device_p, c_char_p], c_char_p), + udev_device_get_devnum=([udev_device_p], dev_t), + udev_device_get_action=([udev_device_p], c_char_p), + udev_device_get_seqnum=([udev_device_p], c_ulonglong), + udev_device_get_is_initialized=([udev_device_p], c_int), + udev_device_get_usec_since_initialized=([udev_device_p], c_ulonglong), + udev_device_get_devlinks_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_tags_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_properties_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_get_sysattr_list_entry=([udev_device_p], udev_list_entry_p), + udev_device_set_sysattr_value=([udev_device_p, c_char_p, c_char_p], c_int), + udev_device_has_tag=([udev_device_p, c_char_p], c_int), + # monitoring + udev_monitor_ref=([udev_monitor_p], udev_monitor_p), + udev_monitor_unref=([udev_monitor_p], None), + udev_monitor_new_from_netlink=([udev_p, c_char_p], udev_monitor_p), + udev_monitor_enable_receiving=([udev_monitor_p], c_int), + udev_monitor_set_receive_buffer_size=([udev_monitor_p, c_int], c_int), + udev_monitor_get_fd=([udev_monitor_p], c_int), + udev_monitor_receive_device=([udev_monitor_p], udev_device_p), + udev_monitor_filter_add_match_subsystem_devtype=( + [udev_monitor_p, c_char_p, c_char_p], c_int), + udev_monitor_filter_add_match_tag=([udev_monitor_p, c_char_p], c_int), + udev_monitor_filter_update=([udev_monitor_p], c_int), + udev_monitor_filter_remove=([udev_monitor_p], c_int), + # hwdb + udev_hwdb_ref=([udev_hwdb_p], udev_hwdb_p), + udev_hwdb_unref=([udev_hwdb_p], None), + udev_hwdb_new=([udev_p], udev_hwdb_p), + udev_hwdb_get_properties_list_entry=( + [udev_hwdb_p, c_char_p, c_uint], + udev_list_entry_p + ) +) + + +ERROR_CHECKERS = dict( + udev_device_get_action=None, + udev_device_get_devlinks_list_entry=None, + udev_device_get_devnode=None, + udev_device_get_devnum=None, + udev_device_get_devpath=None, + udev_device_get_devtype=None, + udev_device_get_driver=None, + udev_device_get_is_initialized=None, + udev_device_get_parent=None, + udev_device_get_parent_with_subsystem_devtype=None, + udev_device_get_properties_list_entry=None, + udev_device_get_property_value=None, + udev_device_get_seqnum=None, + udev_device_get_subsystem=None, + udev_device_get_sysattr_list_entry=None, + udev_device_get_sysattr_value=None, + udev_device_get_sysname=None, + udev_device_get_sysnum=None, + udev_device_get_syspath=None, + udev_device_get_tags_list_entry=None, + udev_device_get_usec_since_initialized=None, + udev_device_has_tag=None, + udev_device_new_from_device_id=None, + udev_device_new_from_devnum=None, + udev_device_new_from_environment=None, + udev_device_new_from_subsystem_sysname=None, + udev_device_new_from_syspath=None, + udev_device_ref=None, + udev_device_unref=None, + udev_device_set_sysattr_value=check_negative_errorcode, + udev_enumerate_add_match_parent=check_negative_errorcode, + udev_enumerate_add_match_subsystem=check_negative_errorcode, + udev_enumerate_add_nomatch_subsystem=check_negative_errorcode, + udev_enumerate_add_match_property=check_negative_errorcode, + udev_enumerate_add_match_sysattr=check_negative_errorcode, + udev_enumerate_add_nomatch_sysattr=check_negative_errorcode, + udev_enumerate_add_match_tag=check_negative_errorcode, + udev_enumerate_add_match_sysname=check_negative_errorcode, + udev_enumerate_add_match_is_initialized=check_negative_errorcode, + udev_enumerate_get_list_entry=None, + udev_enumerate_new=None, + udev_enumerate_ref=None, + udev_enumerate_scan_devices=None, + udev_enumerate_unref=None, + udev_get_dev_path=None, + udev_get_log_priority=None, + udev_get_run_path=None, + udev_get_sys_path=None, + udev_hwdb_get_properties_list_entry=None, + udev_hwdb_new=None, + udev_hwdb_ref=None, + udev_hwdb_unref=None, + udev_list_entry_get_name=None, + udev_list_entry_get_next=None, + udev_list_entry_get_value=None, + udev_monitor_set_receive_buffer_size=check_errno_on_nonzero_return, + # libudev doc says, enable_receiving returns a negative errno, but tests + # show that this is not reliable, so query the real error code + udev_monitor_enable_receiving=check_errno_on_nonzero_return, + udev_monitor_receive_device=check_errno_on_null_pointer_return, + udev_monitor_ref=None, + udev_monitor_filter_add_match_subsystem_devtype=check_negative_errorcode, + udev_monitor_filter_add_match_tag=check_negative_errorcode, + udev_monitor_filter_update=check_errno_on_nonzero_return, + udev_monitor_filter_remove=check_errno_on_nonzero_return, + udev_monitor_get_fd=None, + udev_monitor_new_from_netlink=None, + udev_monitor_unref=None, + udev_new=None, + udev_ref=None, + udev_set_log_priority=None, + udev_unref=None +) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py new file mode 100644 index 0000000..d10b6c9 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_ctypeslib/utils.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._ctypeslib.utils + ======================= + + Utilities for loading ctypeslib. + + .. moduleauthor:: Anne Mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from ctypes import CDLL +from ctypes.util import find_library + + +def load_ctypes_library(name, signatures, error_checkers): + """ + Load library ``name`` and return a :class:`ctypes.CDLL` object for it. + + :param str name: the library name + :param signatures: signatures of methods + :type signatures: dict of str * (tuple of (list of type) * type) + :param error_checkers: error checkers for methods + :type error_checkers: dict of str * ((int * ptr * arglist) -> int) + + The library has errno handling enabled. + Important functions are given proper signatures and return types to support + type checking and argument conversion. + + :returns: a loaded library + :rtype: ctypes.CDLL + :raises ImportError: if the library is not found + """ + library_name = find_library(name) + if not library_name: + raise ImportError('No library named %s' % name) + lib = CDLL(library_name, use_errno=True) + # Add function signatures + for funcname, signature in signatures.items(): + function = getattr(lib, funcname, None) + if function: + argtypes, restype = signature + function.argtypes = argtypes + function.restype = restype + errorchecker = error_checkers.get(funcname) + if errorchecker: + function.errcheck = errorchecker + return lib diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_errors.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_errors.py new file mode 100644 index 0000000..847d908 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_errors.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device._errors + ===================== + + Errors raised by Device methods. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc + +from six import add_metaclass + +@add_metaclass(abc.ABCMeta) +class DeviceError(Exception): + """ + Any error raised when messing around w/ or trying to discover devices. + """ + + +@add_metaclass(abc.ABCMeta) +class DeviceNotFoundError(DeviceError): + """ + An exception indicating that no :class:`Device` was found. + + .. versionchanged:: 0.5 + Rename from ``NoSuchDeviceError`` to its current name. + """ + + +class DeviceNotFoundAtPathError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found at a given path. + """ + + def __init__(self, sys_path): + DeviceNotFoundError.__init__(self, sys_path) + + @property + def sys_path(self): + """ + The path that caused this error as string. + """ + return self.args[0] + + def __str__(self): + return 'No device at {0!r}'.format(self.sys_path) + + +class DeviceNotFoundByFileError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found from the given filename. + """ + +class DeviceNotFoundByInterfaceIndexError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given interface index. + """ + +class DeviceNotFoundByKernelDeviceError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found + from the given kernel device string. + + The format of the kernel device string is defined in the + systemd.journal-fields man pages. + """ + + +class DeviceNotFoundByNameError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was + found with a given name. + """ + + def __init__(self, subsystem, sys_name): + DeviceNotFoundError.__init__(self, subsystem, sys_name) + + @property + def subsystem(self): + """ + The subsystem that caused this error as string. + """ + return self.args[0] + + @property + def sys_name(self): + """ + The sys name that caused this error as string. + """ + return self.args[1] + + def __str__(self): + return 'No device {0.sys_name!r} in {0.subsystem!r}'.format(self) + + +class DeviceNotFoundByNumberError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` was found + for a given device number. + """ + + def __init__(self, typ, number): + DeviceNotFoundError.__init__(self, typ, number) + + @property + def device_type(self): + """ + The device type causing this error as string. Either ``'char'`` or + ``'block'``. + """ + return self.args[0] + + @property + def device_number(self): + """ + The device number causing this error as integer. + """ + return self.args[1] + + def __str__(self): + return ('No {0.device_type} device with number ' + '{0.device_number}'.format(self)) + + +class DeviceNotFoundInEnvironmentError(DeviceNotFoundError): + """ + A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` could + be constructed from the process environment. + """ + + def __str__(self): + return 'No device found in environment' + + +class DeviceValueError(DeviceError): + """ + Raised when a parameter has an unacceptable value. + + May also be raised when the parameter has an unacceptable type. + """ + + _FMT_STR = "value '%s' for parameter %s is unacceptable" + + def __init__(self, value, param, msg=None): + """ Initializer. + + :param object value: the value + :param str param: the parameter + :param str msg: an explanatory message + """ + # pylint: disable=super-init-not-called + self._value = value + self._param = param + self._msg = msg + + def __str__(self): + if self._msg: + fmt_str = self._FMT_STR + ": %s" + return fmt_str % (self._value, self._param, self._msg) + else: + return self._FMT_STR % (self._value, self._param) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py new file mode 100644 index 0000000..3b434ba --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev._os + ========== + + Extras to compensate for deficiencies in python os module. + + .. moduleauthor:: mulhern +""" + +from . import pipe +from . import poll diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py new file mode 100644 index 0000000..41890fb --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/pipe.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.pipe + =============== + + Fallback implementations for pipe. + + 1. pipe2 from python os module + 2. pipe2 from libc + 3. pipe from python os module + + The Pipe class wraps the chosen implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import fcntl +from functools import partial + +from .._ctypeslib.libc import fd_pair +from .._ctypeslib.libc import ERROR_CHECKERS +from .._ctypeslib.libc import SIGNATURES +from .._ctypeslib.utils import load_ctypes_library + +# Define O_CLOEXEC, if not present in os already +O_CLOEXEC = getattr(os, 'O_CLOEXEC', 0o2000000) + + +def _pipe2_ctypes(libc, flags): + """A ``pipe2`` implementation using ``pipe2`` from ctypes. + + ``libc`` is a :class:`ctypes.CDLL` object for libc. ``flags`` is an + integer providing the flags to ``pipe2``. + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = fd_pair() + libc.pipe2(fds, flags) + return fds[0], fds[1] + + +def _pipe2_by_pipe(flags): + """A ``pipe2`` implementation using :func:`os.pipe`. + + ``flags`` is an integer providing the flags to ``pipe2``. + + .. warning:: + + This implementation is not atomic! + + Return a pair of file descriptors ``(r, w)``. + + """ + fds = os.pipe() + if flags & os.O_NONBLOCK != 0: + for fd in fds: + set_fd_status_flag(fd, os.O_NONBLOCK) + if flags & O_CLOEXEC != 0: + for fd in fds: + set_fd_flag(fd, O_CLOEXEC) + return fds + + +def _get_pipe2_implementation(): + """Find the appropriate implementation for ``pipe2``. + +Return a function implementing ``pipe2``.""" + if hasattr(os, 'pipe2'): + return os.pipe2 # pylint: disable=no-member + else: + try: + libc = load_ctypes_library("libc", SIGNATURES, ERROR_CHECKERS) + return (partial(_pipe2_ctypes, libc) + if hasattr(libc, 'pipe2') else + _pipe2_by_pipe) + except ImportError: + return _pipe2_by_pipe + + +_PIPE2 = _get_pipe2_implementation() + + +def set_fd_flag(fd, flag): + """Set a flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFD, 0) + fcntl.fcntl(fd, fcntl.F_SETFD, flags | flag) + + +def set_fd_status_flag(fd, flag): + """Set a status flag on a file descriptor. + + ``fd`` is the file descriptor or file object, ``flag`` the flag as integer. + + """ + flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | flag) + + +class Pipe(object): + """A unix pipe. + + A pipe object provides two file objects: :attr:`source` is a readable file + object, and :attr:`sink` a writeable. Bytes written to :attr:`sink` appear + at :attr:`source`. + + Open a pipe with :meth:`open()`. + + """ + + @classmethod + def open(cls): + """Open and return a new :class:`Pipe`. + + The pipe uses non-blocking IO.""" + source, sink = _PIPE2(os.O_NONBLOCK | O_CLOEXEC) + return cls(source, sink) + + def __init__(self, source_fd, sink_fd): + """Create a new pipe object from the given file descriptors. + + ``source_fd`` is a file descriptor for the readable side of the pipe, + ``sink_fd`` is a file descriptor for the writeable side.""" + self.source = os.fdopen(source_fd, 'rb', 0) + self.sink = os.fdopen(sink_fd, 'wb', 0) + + def close(self): + """Closes both sides of the pipe.""" + try: + self.source.close() + finally: + self.sink.close() diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/poll.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/poll.py new file mode 100644 index 0000000..e760a36 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_os/poll.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._os.poll + =============== + + Operating system interface for pyudev. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import select + +from .._util import eintr_retry_call + + +class Poll(object): + """A poll object. + + This object essentially provides a more convenient interface around + :class:`select.poll`. + + """ + + _EVENT_TO_MASK = {'r': select.POLLIN, + 'w': select.POLLOUT} + + @staticmethod + def _has_event(events, event): + return events & event != 0 + + @classmethod + def for_events(cls, *events): + """Listen for ``events``. + + ``events`` is a list of ``(fd, event)`` pairs, where ``fd`` is a file + descriptor or file object and ``event`` either ``'r'`` or ``'w'``. If + ``r``, listen for whether that is ready to be read. If ``w``, listen + for whether the channel is ready to be written to. + + """ + notifier = eintr_retry_call(select.poll) + for fd, event in events: + mask = cls._EVENT_TO_MASK.get(event) + if not mask: + raise ValueError('Unknown event type: {0!r}'.format(event)) + notifier.register(fd, mask) + return cls(notifier) + + def __init__(self, notifier): + """Create a poll object for the given ``notifier``. + + ``notifier`` is the :class:`select.poll` object wrapped by the new poll + object. + + """ + self._notifier = notifier + + def poll(self, timeout=None): + """Poll for events. + + ``timeout`` is an integer specifying how long to wait for events (in + milliseconds). If omitted, ``None`` or negative, wait until an event + occurs. + + Return a list of all events that occurred before ``timeout``, where + each event is a pair ``(fd, event)``. ``fd`` is the integral file + descriptor, and ``event`` a string indicating the event type. If + ``'r'``, there is data to read from ``fd``. If ``'w'``, ``fd`` is + writable without blocking now. If ``'h'``, the file descriptor was + hung up (i.e. the remote side of a pipe was closed). + + """ + # Return a list to allow clients to determine whether there are any + # events at all with a simple truthiness test. + return list(self._parse_events(eintr_retry_call(self._notifier.poll, timeout))) + + def _parse_events(self, events): + """Parse ``events``. + + ``events`` is a list of events as returned by + :meth:`select.poll.poll()`. + + Yield all parsed events. + + """ + for fd, event_mask in events: + if self._has_event(event_mask, select.POLLNVAL): + raise IOError('File descriptor not open: {0!r}'.format(fd)) + elif self._has_event(event_mask, select.POLLERR): + raise IOError('Error while polling fd: {0!r}'.format(fd)) + + if self._has_event(event_mask, select.POLLIN): + yield fd, 'r' + if self._has_event(event_mask, select.POLLOUT): + yield fd, 'w' + if self._has_event(event_mask, select.POLLHUP): + yield fd, 'h' diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_qt_base.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_qt_base.py new file mode 100644 index 0000000..dd227f2 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_qt_base.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._qt_base + =============== + + Base mixin class for Qt4,Qt5 support. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six + +from pyudev.device import Device + +class MonitorObserverMixin(object): + """ + Base mixin for pyqt monitor observers. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + self.monitor = monitor + self.notifier = notifier_class( + monitor.fileno(), notifier_class.Read, self) + self.notifier.activated[int].connect(self._process_udev_event) + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. This merely + reflects the state of the ``enabled`` property of the underlying + :attr:`notifier`. + + .. versionadded:: 0.14 + """ + return self.notifier.isEnabled() + + @enabled.setter + def enabled(self, value): + self.notifier.setEnabled(value) + + def _process_udev_event(self): + """ + Attempt to receive a single device event from the monitor, process + the event and emit corresponding signals. + + Called by ``QSocketNotifier``, if data is available on the udev + monitoring socket. + """ + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + + def _emit_event(self, device): + self.deviceEvent.emit(device) + + +class QUDevMonitorObserverMixin(MonitorObserverMixin): + """ + Obsolete monitor observer mixin. + """ + # pylint: disable=too-few-public-methods + + def _setup_notifier(self, monitor, notifier_class): + MonitorObserverMixin._setup_notifier(self, monitor, notifier_class) + self._action_signal_map = { + 'add': self.deviceAdded, 'remove': self.deviceRemoved, + 'change': self.deviceChanged, 'move': self.deviceMoved, + } + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.pyqt4.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.deviceEvent.emit(device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + signal.emit(device) + +def make_init(qobject, socket_notifier): + """ + Generates an initializer to observer the given ``monitor`` + (a :class:`~pyudev.Monitor`): + + ``parent`` is the parent :class:`~PyQt{4,5}.QtCore.QObject` of this + object. It is passed unchanged to the inherited constructor of + :class:`~PyQt{4,5}.QtCore.QObject`. + """ + + def __init__(self, monitor, parent=None): + qobject.__init__(self, parent) + # pylint: disable=protected-access + self._setup_notifier(monitor, socket_notifier) + + return __init__ + + +class MonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PySide, PyQt{4,5}}.QtCore.QObject`. + + """ + return type( + str("MonitorObserver"), + (qobject, MonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + str("deviceEvent") : signal(Device) + } + ) + + + +class QUDevMonitorObserverGenerator(object): + """ + Class to generate a MonitorObserver class. + """ + # pylint: disable=too-few-public-methods + + @staticmethod + def make_monitor_observer(qobject, signal, socket_notifier): + """Generates an observer for device events integrating into the + PyQt{4,5} mainloop. + + This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device + events into Qt signals: + + >>> from pyudev import Context, Monitor + >>> from pyudev.pyqt4 import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.deviceEvent.connect(device_event) + >>> monitor.start() + + This class is a child of :class:`~{PyQt{4,5}, PySide}.QtCore.QObject`. + + """ + return type( + str("QUDevMonitorObserver"), + (qobject, QUDevMonitorObserverMixin), + { + str("__init__") : make_init(qobject, socket_notifier), + #: emitted upon arbitrary device events + str("deviceEvent") : signal(six.text_type, Device), + #: emitted if a device was added + str("deviceAdded") : signal(Device), + #: emitted if a device was removed + str("deviceRemoved") : signal(Device), + #: emitted if a device was changed + str("deviceChanged") : signal(Device), + #: emitted if a device was moved + str("deviceMoved") : signal(Device) + } + ) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_util.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_util.py new file mode 100644 index 0000000..d6d736c --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/_util.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev._util + ============ + + Internal utilities + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +try: + from subprocess import check_output +except ImportError: + from pyudev._compat import check_output + +import os +import sys +import stat +import errno + +import six + + +def ensure_byte_string(value): + """ + Return the given ``value`` as bytestring. + + If the given ``value`` is not a byte string, but a real unicode string, it + is encoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, bytes): + value = value.encode(sys.getfilesystemencoding()) + return value + + +def ensure_unicode_string(value): + """ + Return the given ``value`` as unicode string. + + If the given ``value`` is not a unicode string, but a byte string, it is + decoded with the filesystem encoding (as in + :func:`sys.getfilesystemencoding()`). + """ + if not isinstance(value, six.text_type): + value = value.decode(sys.getfilesystemencoding()) + return value + + +def property_value_to_bytes(value): + """ + Return a byte string, which represents the given ``value`` in a way + suitable as raw value of an udev property. + + If ``value`` is a boolean object, it is converted to ``'1'`` or ``'0'``, + depending on whether ``value`` is ``True`` or ``False``. If ``value`` is a + byte string already, it is returned unchanged. Anything else is simply + converted to a unicode string, and then passed to + :func:`ensure_byte_string`. + """ + # udev represents boolean values as 1 or 0, therefore an explicit + # conversion to int is required for boolean values + if isinstance(value, bool): + value = int(value) + if isinstance(value, bytes): + return value + else: + return ensure_byte_string(six.text_type(value)) + + +def string_to_bool(value): + """ + Convert the given unicode string ``value`` to a boolean object. + + If ``value`` is ``'1'``, ``True`` is returned. If ``value`` is ``'0'``, + ``False`` is returned. Any other value raises a + :exc:`~exceptions.ValueError`. + """ + if value not in ('1', '0'): + raise ValueError('Not a boolean value: {0!r}'.format(value)) + return value == '1' + + +def udev_list_iterate(libudev, entry): + """ + Iteration helper for udev list entry objects. + + Yield a tuple ``(name, value)``. ``name`` and ``value`` are bytestrings + containing the name and the value of the list entry. The exact contents + depend on the list iterated over. + """ + while entry: + name = libudev.udev_list_entry_get_name(entry) + value = libudev.udev_list_entry_get_value(entry) + yield (name, value) + entry = libudev.udev_list_entry_get_next(entry) + + +def get_device_type(filename): + """ + Get the device type of a device file. + + ``filename`` is a string containing the path of a device file. + + Return ``'char'`` if ``filename`` is a character device, or ``'block'`` if + ``filename`` is a block device. Raise :exc:`~exceptions.ValueError` if + ``filename`` is no device file at all. Raise + :exc:`~exceptions.EnvironmentError` if ``filename`` does not exist or if + its metadata was inaccessible. + + .. versionadded:: 0.15 + """ + mode = os.stat(filename).st_mode + if stat.S_ISCHR(mode): + return 'char' + elif stat.S_ISBLK(mode): + return 'block' + else: + raise ValueError('not a device file: {0!r}'.format(filename)) + + +def eintr_retry_call(func, *args, **kwargs): + """ + Handle interruptions to an interruptible system call. + + Run an interruptible system call in a loop and retry if it raises EINTR. + The signal calls that may raise EINTR prior to Python 3.5 are listed in + PEP 0475. Any calls to these functions must be wrapped in eintr_retry_call + in order to handle EINTR returns in older versions of Python. + + This function is safe to use under Python 3.5 and newer since the wrapped + function will simply return without raising EINTR. + + This function is based on _eintr_retry_call in python's subprocess.py. + """ + + # select.error inherits from Exception instead of OSError in Python 2 + import select + + while True: + try: + return func(*args, **kwargs) + except (OSError, IOError, select.error) as err: + # If this is not an IOError or OSError, it's the old select.error + # type, which means that the errno is only accessible via subscript + if isinstance(err, (OSError, IOError)): + error_code = err.errno + else: + error_code = err.args[0] + + if error_code == errno.EINTR: + continue + raise + +def udev_version(): + """ + Get the version of the underlying udev library. + + udev doesn't use a standard major-minor versioning scheme, but instead + labels releases with a single consecutive number. Consequently, the + version number returned by this function is a single integer, and not a + tuple (like for instance the interpreter version in + :data:`sys.version_info`). + + As libudev itself does not provide a function to query the version number, + this function calls the ``udevadm`` utility, so be prepared to catch + :exc:`~exceptions.EnvironmentError` and + :exc:`~subprocess.CalledProcessError` if you call this function. + + Return the version number as single integer. Raise + :exc:`~exceptions.ValueError`, if the version number retrieved from udev + could not be converted to an integer. Raise + :exc:`~exceptions.EnvironmentError`, if ``udevadm`` was not found, or could + not be executed. Raise :exc:`subprocess.CalledProcessError`, if + ``udevadm`` returned a non-zero exit code. On Python 2.7 or newer, the + ``output`` attribute of this exception is correctly set. + + .. versionadded:: 0.8 + """ + output = ensure_unicode_string(check_output(['udevadm', '--version'])) + return int(output.strip()) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/core.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/core.py new file mode 100644 index 0000000..9cb6dbe --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/core.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.core + =========== + + Core types and functions of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from .device import Devices +from ._errors import DeviceNotFoundAtPathError +from ._ctypeslib.libudev import ERROR_CHECKERS +from ._ctypeslib.libudev import SIGNATURES +from ._ctypeslib.utils import load_ctypes_library + +from ._util import ensure_byte_string +from ._util import ensure_unicode_string +from ._util import property_value_to_bytes +from ._util import udev_list_iterate + + +class Context(object): + """ + A device database connection. + + This class represents a connection to the udev device database, and is + really *the* central object to access udev. You need an instance of this + class for almost anything else in pyudev. + + This class itself gives access to various udev configuration data (e.g. + :attr:`sys_path`, :attr:`device_path`), and provides device enumeration + (:meth:`list_devices()`). + + Instances of this class can directly be given as ``udev *`` to functions + wrapped through :mod:`ctypes`. + """ + + def __init__(self): + """ + Create a new context. + """ + self._libudev = load_ctypes_library('udev', SIGNATURES, ERROR_CHECKERS) + self._as_parameter_ = self._libudev.udev_new() + + def __del__(self): + self._libudev.udev_unref(self) + + @property + def sys_path(self): + """ + The ``sysfs`` mount point defaulting to ``/sys'`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_sys_path'): + return ensure_unicode_string(self._libudev.udev_get_sys_path(self)) + else: + # Fixed path since udev 183 + return '/sys' + + @property + def device_path(self): + """ + The device directory path defaulting to ``/dev`` as unicode string. + """ + if hasattr(self._libudev, 'udev_get_dev_path'): + return ensure_unicode_string(self._libudev.udev_get_dev_path(self)) + else: + # Fixed path since udev 183 + return '/dev' + + @property + def run_path(self): + """ + The run runtime directory path defaulting to ``/run`` as unicode + string. + + .. udevversion:: 167 + + .. versionadded:: 0.10 + """ + if hasattr(self._libudev, 'udev_get_run_path'): + return ensure_unicode_string(self._libudev.udev_get_run_path(self)) + else: + return '/run/udev' + + @property + def log_priority(self): + """ + The logging priority of the interal logging facitility of udev as + integer with a standard :mod:`syslog` priority. Assign to this + property to change the logging priority. + + UDev uses the standard :mod:`syslog` priorities. Constants for these + priorities are defined in the :mod:`syslog` module in the standard + library: + + >>> import syslog + >>> context = pyudev.Context() + >>> context.log_priority = syslog.LOG_DEBUG + + .. versionadded:: 0.9 + """ + return self._libudev.udev_get_log_priority(self) + + @log_priority.setter + def log_priority(self, value): + """ + Set the log priority. + + :param int value: the log priority. + """ + self._libudev.udev_set_log_priority(self, value) + + def list_devices(self, **kwargs): + """ + List all available devices. + + The arguments of this method are the same as for + :meth:`Enumerator.match()`. In fact, the arguments are simply passed + straight to method :meth:`~Enumerator.match()`. + + This function creates and returns an :class:`Enumerator` object, + that can be used to filter the list of devices, and eventually + retrieve :class:`Device` objects representing matching devices. + + .. versionchanged:: 0.8 + Accept keyword arguments now for easy matching. + """ + return Enumerator(self).match(**kwargs) + + +class Enumerator(object): + """ + A filtered iterable of devices. + + To retrieve devices, simply iterate over an instance of this class. + This operation yields :class:`Device` objects representing the available + devices. + + Before iteration the device list can be filtered by subsystem or by + property values using :meth:`match_subsystem` and + :meth:`match_property`. Multiple subsystem (property) filters are + combined using a logical OR, filters of different types are combined + using a logical AND. The following filter for instance:: + + devices.match_subsystem('block').match_property( + 'ID_TYPE', 'disk').match_property('DEVTYPE', 'disk') + + means the following:: + + subsystem == 'block' and (ID_TYPE == 'disk' or DEVTYPE == 'disk') + + Once added, a filter cannot be removed anymore. Create a new object + instead. + + Instances of this class can directly be given as given ``udev_enumerate *`` + to functions wrapped through :mod:`ctypes`. + """ + + def __init__(self, context): + """ + Create a new enumerator with the given ``context`` (a + :class:`Context` instance). + + While you can create objects of this class directly, this is not + recommended. Call :method:`Context.list_devices()` instead. + """ + if not isinstance(context, Context): + raise TypeError('Invalid context object') + self.context = context + self._as_parameter_ = context._libudev.udev_enumerate_new(context) + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_enumerate_unref(self) + + def match(self, **kwargs): + """ + Include devices according to the rules defined by the keyword + arguments. These keyword arguments are interpreted as follows: + + - The value for the keyword argument ``subsystem`` is forwarded to + :meth:`match_subsystem()`. + - The value for the keyword argument ``sys_name`` is forwared to + :meth:`match_sys_name()`. + - The value for the keyword argument ``tag`` is forwared to + :meth:`match_tag()`. + - The value for the keyword argument ``parent`` is forwared to + :meth:`match_parent()`. + - All other keyword arguments are forwareded one by one to + :meth:`match_property()`. The keyword argument itself is interpreted + as property name, the value of the keyword argument as the property + value. + + All keyword arguments are optional, calling this method without no + arguments at all is simply a noop. + + Return the instance again. + + .. versionadded:: 0.8 + + .. versionchanged:: 0.13 + Add ``parent`` keyword. + """ + subsystem = kwargs.pop('subsystem', None) + if subsystem is not None: + self.match_subsystem(subsystem) + sys_name = kwargs.pop('sys_name', None) + if sys_name is not None: + self.match_sys_name(sys_name) + tag = kwargs.pop('tag', None) + if tag is not None: + self.match_tag(tag) + parent = kwargs.pop('parent', None) + if parent is not None: + self.match_parent(parent) + for prop, value in kwargs.items(): + self.match_property(prop, value) + return self + + def match_subsystem(self, subsystem, nomatch=False): + """ + Include all devices, which are part of the given ``subsystem``. + + ``subsystem`` is either a unicode string or a byte string, containing + the name of the subsystem. If ``nomatch`` is ``True`` (default is + ``False``), the match is inverted: A device is only included if it is + *not* part of the given ``subsystem``. + + Note that, if a device has no subsystem, it is not included either + with value of ``nomatch`` True or with value of ``nomatch`` False. + + Return the instance again. + """ + match = self._libudev.udev_enumerate_add_nomatch_subsystem \ + if nomatch else self._libudev.udev_enumerate_add_match_subsystem + match(self, ensure_byte_string(subsystem)) + return self + + def match_sys_name(self, sys_name): + """ + Include all devices with the given name. + + ``sys_name`` is a byte or unicode string containing the device name. + + Return the instance again. + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_sysname( + self, ensure_byte_string(sys_name)) + return self + + def match_property(self, prop, value): + """ + Include all devices, whose ``prop`` has the given ``value``. + + ``prop`` is either a unicode string or a byte string, containing + the name of the property to match. ``value`` is a property value, + being one of the following types: + + - :func:`int` + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + Return the instance again. + """ + self._libudev.udev_enumerate_add_match_property( + self, ensure_byte_string(prop), property_value_to_bytes(value)) + return self + + def match_attribute(self, attribute, value, nomatch=False): + """ + Include all devices, whose ``attribute`` has the given ``value``. + + ``attribute`` is either a unicode string or a byte string, containing + the name of a sys attribute to match. ``value`` is an attribute value, + being one of the following types: + + - :func:`int`, + - :func:`bool` + - A byte string + - Anything convertable to a unicode string (including a unicode string + itself) + + If ``nomatch`` is ``True`` (default is ``False``), the match is + inverted: A device is include if the ``attribute`` does *not* match + the given ``value``. + + .. note:: + + If ``nomatch`` is ``True``, devices which do not have the given + ``attribute`` at all are also included. In other words, with + ``nomatch=True`` the given ``attribute`` is *not* guaranteed to + exist on all returned devices. + + Return the instance again. + """ + match = (self._libudev.udev_enumerate_add_match_sysattr + if not nomatch else + self._libudev.udev_enumerate_add_nomatch_sysattr) + match(self, ensure_byte_string(attribute), + property_value_to_bytes(value)) + return self + + def match_tag(self, tag): + """ + Include all devices, which have the given ``tag`` attached. + + ``tag`` is a byte or unicode string containing the tag name. + + Return the instance again. + + .. udevversion:: 154 + + .. versionadded:: 0.6 + """ + self._libudev.udev_enumerate_add_match_tag(self, ensure_byte_string(tag)) + return self + + def match_is_initialized(self): + """ + Include only devices, which are initialized. + + Initialized devices have properly set device node permissions and + context, and are (in case of network devices) fully renamed. + + Currently this will not affect devices which do not have device nodes + and are not network interfaces. + + Return the instance again. + + .. seealso:: :attr:`Device.is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + self._libudev.udev_enumerate_add_match_is_initialized(self) + return self + + def match_parent(self, parent): + """ + Include all devices on the subtree of the given ``parent`` device. + + The ``parent`` device itself is also included. + + ``parent`` is a :class:`~pyudev.Device`. + + Return the instance again. + + .. udevversion:: 172 + + .. versionadded:: 0.13 + """ + self._libudev.udev_enumerate_add_match_parent(self, parent) + return self + + def __iter__(self): + """ + Iterate over all matching devices. + + Yield :class:`Device` objects. + """ + self._libudev.udev_enumerate_scan_devices(self) + entry = self._libudev.udev_enumerate_get_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, entry): + try: + yield Devices.from_sys_path(self.context, name) + except DeviceNotFoundAtPathError: + continue diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/__init__.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/__init__.py new file mode 100644 index 0000000..14c9c63 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.device + ============= + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from ._device import Attributes +from ._device import Device +from ._device import Devices +from ._device import Tags diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/_device.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/_device.py new file mode 100644 index 0000000..94dd41c --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/device/_device.py @@ -0,0 +1,1282 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2011, 2012 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.device._device + ===================== + + Device class implementation of :mod:`pyudev`. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import re +from collections import Container +from collections import Iterable +from collections import Mapping +from datetime import timedelta + +from .._errors import DeviceNotFoundAtPathError +from .._errors import DeviceNotFoundByFileError +from .._errors import DeviceNotFoundByInterfaceIndexError +from .._errors import DeviceNotFoundByKernelDeviceError +from .._errors import DeviceNotFoundByNameError +from .._errors import DeviceNotFoundByNumberError +from .._errors import DeviceNotFoundInEnvironmentError +from .._util import ensure_byte_string +from .._util import ensure_unicode_string +from .._util import get_device_type +from .._util import string_to_bool +from .._util import udev_list_iterate + +# pylint: disable=too-many-lines + +class Devices(object): + """ + Class for constructing :class:`Device` objects from various kinds of data. + """ + + @classmethod + def from_path(cls, context, path): + """ + Create a device from a device ``path``. The ``path`` may or may not + start with the ``sysfs`` mount point: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_path(context, '/devices/platform') + Device(u'/sys/devices/platform') + >>> Devices.from_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``path`` is a device path as unicode or byte string. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for ``path``. + + .. versionadded:: 0.18 + """ + if not path.startswith(context.sys_path): + path = os.path.join(context.sys_path, path.lstrip(os.sep)) + return cls.from_sys_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): + """ + Create a new device from a given ``sys_path``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> Devices.from_sys_path(context, '/sys/devices/platform') + Device(u'/sys/devices/platform') + + ``context`` is the :class:`Context` in which to search the device. + ``sys_path`` is a unicode or byte string containing the path of the + device inside ``sysfs`` with the mount point included. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundAtPathError`, if no device was found for + ``sys_path``. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_syspath( + context, ensure_byte_string(sys_path)) + if not device: + raise DeviceNotFoundAtPathError(sys_path) + return Device(context, device) + + @classmethod + def from_name(cls, context, subsystem, sys_name): + """ + Create a new device from a given ``subsystem`` and a given + ``sys_name``: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda + Device(u'/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda') + >>> sda == Devices.from_path(context, '/block/sda') + + ``context`` is the :class:`Context` in which to search the device. + ``subsystem`` and ``sys_name`` are byte or unicode strings, which + denote the subsystem and the name of the device to create. + + Return a :class:`Device` object for the device. Raise + :exc:`DeviceNotFoundByNameError`, if no device was found with the given + name. + + .. versionadded:: 0.18 + """ + sys_name = sys_name.replace("/", "!") + device = context._libudev.udev_device_new_from_subsystem_sysname( + context, ensure_byte_string(subsystem), + ensure_byte_string(sys_name)) + if not device: + raise DeviceNotFoundByNameError(subsystem, sys_name) + return Device(context, device) + + @classmethod + def from_device_number(cls, context, typ, number): + """ + Create a new device from a device ``number`` with the given device + ``type``: + + >>> import os + >>> from pyudev import Context, Device + >>> ctx = Context() + >>> major, minor = 8, 0 + >>> device = Devices.from_device_number(context, 'block', + ... os.makedev(major, minor)) + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:11.0/host0/target0:0:0/0:0:0:0/block/sda') + >>> os.major(device.device_number), os.minor(device.device_number) + (8, 0) + + Use :func:`os.makedev` to construct a device number from a major and a + minor device number, as shown in the example above. + + .. warning:: + + Device numbers are not unique across different device types. + Passing a correct number with a wrong type may silently yield a + wrong device object, so make sure to pass the correct device type. + + ``context`` is the :class:`Context`, in which to search the device. + ``type`` is either ``'char'`` or ``'block'``, according to whether the + device is a character or block device. ``number`` is the device number + as integer. + + Return a :class:`Device` object for the device with the given device + ``number``. Raise :exc:`DeviceNotFoundByNumberError`, if no device was + found with the given device type and number. + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_devnum( + context, ensure_byte_string(typ[0]), number) + if not device: + raise DeviceNotFoundByNumberError(typ, number) + return Device(context, device) + + @classmethod + def from_device_file(cls, context, filename): + """ + Create a new device from the given device file: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_device_file(context, '/dev/sda') + >>> device + Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') + >>> device.device_node + u'/dev/sda' + + .. warning:: + + Though the example seems to suggest that ``device.device_node == + filename`` holds with ``device = Devices.from_device_file(context, + filename)``, this is only true in a majority of cases. There *can* + be devices, for which this relation is actually false! Thus, do + *not* expect :attr:`~Device.device_node` to be equal to the given + ``filename`` for the returned :class:`Device`. Especially, use + :attr:`~Device.device_node` if you need the device file of a + :class:`Device` created with this method afterwards. + + ``context`` is the :class:`Context` in which to search the device. + ``filename`` is a string containing the path of a device file. + + Return a :class:`Device` representing the given device file. Raise + :exc:`DeviceNotFoundByFileError` if ``filename`` is no device file + at all or if ``filename`` does not exist or if its metadata was + inaccessible. + + .. versionadded:: 0.18 + """ + try: + device_type = get_device_type(filename) + device_number = os.stat(filename).st_rdev + except (EnvironmentError, ValueError) as err: + raise DeviceNotFoundByFileError(err) + + return cls.from_device_number(context, device_type, device_number) + + + @classmethod + def from_interface_index(cls, context, ifindex): + """ + Locate a device based on the interface index. + + :param `Context` context: the libudev context + :param int ifindex: the interface index + :returns: the device corresponding to the interface index + :rtype: `Device` + + This method is only appropriate for network devices. + """ + network_devices = context.list_devices(subsystem='net') + dev = next( + (d for d in network_devices if \ + d.attributes.get('ifindex') == ifindex), + None + ) + if dev is not None: + return dev + else: + raise DeviceNotFoundByInterfaceIndexError(ifindex) + + + @classmethod + def from_kernel_device(cls, context, kernel_device): + """ + Locate a device based on the kernel device. + + :param `Context` context: the libudev context + :param str kernel_device: the kernel device + :returns: the device corresponding to ``kernel_device`` + :rtype: `Device` + """ + switch_char = kernel_device[0] + rest = kernel_device[1:] + if switch_char in ('b', 'c'): + number_re = re.compile(r'^(?P\d+):(?P\d+)$') + match = number_re.match(rest) + if match: + number = os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + return cls.from_device_number(context, switch_char, number) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + elif switch_char == 'n': + return cls.from_interface_index(context, rest) + elif switch_char == '+': + (subsystem, _, kernel_device_name) = rest.partition(':') + if kernel_device_name and subsystem: + return cls.from_name(context, subsystem, kernel_device_name) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + else: + raise DeviceNotFoundByKernelDeviceError(kernel_device) + + + @classmethod + def from_environment(cls, context): + """ + Create a new device from the process environment (as in + :data:`os.environ`). + + This only works reliable, if the current process is called from an + udev rule, and is usually used for tools executed from ``IMPORT=`` + rules. Use this method to create device objects in Python scripts + called from udev rules. + + ``context`` is the library :class:`Context`. + + Return a :class:`Device` object constructed from the environment. + Raise :exc:`DeviceNotFoundInEnvironmentError`, if no device could be + created from the environment. + + .. udevversion:: 152 + + .. versionadded:: 0.18 + """ + device = context._libudev.udev_device_new_from_environment(context) + if not device: + raise DeviceNotFoundInEnvironmentError() + return Device(context, device) + + @classmethod + def METHODS(cls): # pylint: disable=invalid-name + """ + Return methods that obtain a :class:`Device` from a variety of + different data. + + :return: a list of from_* methods. + :rtype: list of class methods + + .. versionadded:: 0.18 + """ + return [ #pragma: no cover + cls.from_device_file, + cls.from_device_number, + cls.from_name, + cls.from_path, + cls.from_sys_path + ] + + +class Device(Mapping): + # pylint: disable=too-many-public-methods + """ + A single device with attached attributes and properties. + + A device also has a set of udev-specific attributes like the path + inside ``sysfs``. + + :class:`Device` objects compare equal and unequal to other devices and + to strings (based on :attr:`device_path`). However, there is no + ordering on :class:`Device` objects, and the corresponding operators + ``>``, ``<``, ``<=`` and ``>=`` raise :exc:`~exceptions.TypeError`. + + .. warning:: + + Currently, Device extends Mapping. The mapping that it stores is that + of udev property names to udev property values. This use is deprecated + and Device will no longer extend Mapping in 1.0. To look up udev + properties, use the Device.properties property. + + .. warning:: + + **Never** use object identity (``is`` operator) to compare + :class:`Device` objects. :mod:`pyudev` may create multiple + :class:`Device` objects for the same device. Instead compare + devices by value using ``==`` or ``!=``. + + :class:`Device` objects are hashable and can therefore be used as keys + in dictionaries and sets. + + They can also be given directly as ``udev_device *`` to functions wrapped + through :mod:`ctypes`. + """ + + @classmethod + def from_path(cls, context, path): #pragma: no cover + """ + .. versionadded:: 0.4 + .. deprecated:: 0.18 + Use :class:`Devices.from_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_path(context, path) + + @classmethod + def from_sys_path(cls, context, sys_path): #pragma: no cover + """ + .. versionchanged:: 0.4 + Raise :exc:`NoSuchDeviceError` instead of returning ``None``, if + no device was found for ``sys_path``. + .. versionchanged:: 0.5 + Raise :exc:`DeviceNotFoundAtPathError` instead of + :exc:`NoSuchDeviceError`. + .. deprecated:: 0.18 + Use :class:`Devices.from_sys_path` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_sys_path(context, sys_path) + + @classmethod + def from_name(cls, context, subsystem, sys_name): #pragma: no cover + """ + .. versionadded:: 0.5 + .. deprecated:: 0.18 + Use :class:`Devices.from_name` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_name(context, subsystem, sys_name) + + @classmethod + def from_device_number(cls, context, typ, number): #pragma: no cover + """ + .. versionadded:: 0.11 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_number` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_number(context, typ, number) + + @classmethod + def from_device_file(cls, context, filename): #pragma: no cover + """ + .. versionadded:: 0.15 + .. deprecated:: 0.18 + Use :class:`Devices.from_device_file` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_device_file(context, filename) + + @classmethod + def from_environment(cls, context): #pragma: no cover + """ + .. versionadded:: 0.6 + .. deprecated:: 0.18 + Use :class:`Devices.from_environment` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use equivalent Devices method instead.', + DeprecationWarning, + stacklevel=2 + ) + return Devices.from_environment(context) + + def __init__(self, context, _device): + self.context = context + self._as_parameter_ = _device + self._libudev = context._libudev + + def __del__(self): + self._libudev.udev_device_unref(self) + + def __repr__(self): + return 'Device({0.sys_path!r})'.format(self) + + @property + def parent(self): + """ + The parent :class:`Device` or ``None``, if there is no parent + device. + """ + parent = self._libudev.udev_device_get_parent(self) + if not parent: + return None + # the parent device is not referenced, thus forcibly acquire a + # reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + @property + def children(self): + """ + Yield all direct children of this device. + + .. note:: + + In udev, parent-child relationships are generally ambiguous, i.e. + a parent can have multiple children, *and* a child can have multiple + parents. Hence, `child.parent == parent` does generally *not* hold + for all `child` objects in `parent.children`. In other words, + the :attr:`parent` of a device in this property can be different + from this device! + + .. note:: + + As the underlying library does not provide any means to directly + query the children of a device, this property performs a linear + search through all devices. + + Return an iterable yielding a :class:`Device` object for each direct + child of this device. + + .. udevversion:: 172 + + .. versionchanged:: 0.13 + Requires udev version 172 now. + """ + for device in self.context.list_devices().match_parent(self): + if device != self: + yield device + + @property + def ancestors(self): + """ + Yield all ancestors of this device from bottom to top. + + Return an iterator yielding a :class:`Device` object for each + ancestor of this device from bottom to top. + + .. versionadded:: 0.16 + """ + parent = self.parent + while parent is not None: + yield parent + parent = parent.parent + + def find_parent(self, subsystem, device_type=None): + """ + Find the parent device with the given ``subsystem`` and + ``device_type``. + + ``subsystem`` is a byte or unicode string containing the name of the + subsystem, in which to search for the parent. ``device_type`` is a + byte or unicode string holding the expected device type of the parent. + It can be ``None`` (the default), which means, that no specific device + type is expected. + + Return a parent :class:`Device` within the given ``subsystem`` and, if + ``device_type`` is not ``None``, with the given ``device_type``, or + ``None``, if this device has no parent device matching these + constraints. + + .. versionadded:: 0.9 + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + parent = self._libudev.udev_device_get_parent_with_subsystem_devtype( + self, subsystem, device_type) + if not parent: + return None + # parent device is not referenced, thus forcibly acquire a reference + return Device(self.context, self._libudev.udev_device_ref(parent)) + + def traverse(self): + """ + Traverse all parent devices of this device from bottom to top. + + Return an iterable yielding all parent devices as :class:`Device` + objects, *not* including the current device. The last yielded + :class:`Device` is the top of the device hierarchy. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :attr:`ancestors` instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.ancestors instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.ancestors + + @property + def sys_path(self): + """ + Absolute path of this device in ``sysfs`` including the ``sysfs`` + mount point as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_syspath(self)) + + @property + def device_path(self): + """ + Kernel device path as unicode string. This path uniquely identifies + a single device. + + Unlike :attr:`sys_path`, this path does not contain the ``sysfs`` + mount point. However, the path is absolute and starts with a slash + ``'/'``. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_devpath(self)) + + @property + def subsystem(self): + """ + Name of the subsystem this device is part of as unicode string. + + :returns: name of subsystem if found, else None + :rtype: unicode string or NoneType + """ + subsys = self._libudev.udev_device_get_subsystem(self) + return None if subsys is None else ensure_unicode_string(subsys) + + @property + def sys_name(self): + """ + Device file name inside ``sysfs`` as unicode string. + """ + return ensure_unicode_string( + self._libudev.udev_device_get_sysname(self)) + + @property + def sys_number(self): + """ + The trailing number of the :attr:`sys_name` as unicode string, or + ``None``, if the device has no trailing number in its name. + + .. note:: + + The number is returned as unicode string to preserve the exact + format of the number, especially any leading zeros: + + >>> from pyudev import Context, Device + >>> context = Context() + >>> device = Devices.from_path(context, '/sys/devices/LNXSYSTM:00') + >>> device.sys_number + u'00' + + To work with numbers, explicitly convert them to ints: + + >>> int(device.sys_number) + 0 + + .. versionadded:: 0.11 + """ + number = self._libudev.udev_device_get_sysnum(self) + return ensure_unicode_string(number) if number is not None else None + + @property + def device_type(self): + """ + Device type as unicode string, or ``None``, if the device type is + unknown. + + >>> from pyudev import Context + >>> context = Context() + >>> for device in context.list_devices(subsystem='net'): + ... '{0} - {1}'.format(device.sys_name, device.device_type or 'ethernet') + ... + u'eth0 - ethernet' + u'wlan0 - wlan' + u'lo - ethernet' + u'vboxnet0 - ethernet' + + .. versionadded:: 0.10 + """ + device_type = self._libudev.udev_device_get_devtype(self) + if device_type is not None: + return ensure_unicode_string(device_type) + else: + return device_type + + @property + def driver(self): + """ + The driver name as unicode string, or ``None``, if there is no + driver for this device. + + .. versionadded:: 0.5 + """ + driver = self._libudev.udev_device_get_driver(self) + return ensure_unicode_string(driver) if driver is not None else None + + @property + def device_node(self): + """ + Absolute path to the device node of this device as unicode string or + ``None``, if this device doesn't have a device node. The path + includes the device directory (see :attr:`Context.device_path`). + + This path always points to the actual device node associated with + this device, and never to any symbolic links to this device node. + See :attr:`device_links` to get a list of symbolic links to this + device node. + + .. warning:: + + For devices created with :meth:`from_device_file()`, the value of + this property is not necessary equal to the ``filename`` given to + :meth:`from_device_file()`. + """ + node = self._libudev.udev_device_get_devnode(self) + return ensure_unicode_string(node) if node is not None else None + + @property + def device_number(self): + """ + The device number of the associated device as integer, or ``0``, if no + device number is associated. + + Use :func:`os.major` and :func:`os.minor` to decompose the device + number into its major and minor number: + + >>> import os + >>> from pyudev import Context, Device + >>> context = Context() + >>> sda = Devices.from_name(context, 'block', 'sda') + >>> sda.device_number + 2048L + >>> (os.major(sda.device_number), os.minor(sda.device_number)) + (8, 0) + + For devices with an associated :attr:`device_node`, this is the same as + the ``st_rdev`` field of the stat result of the :attr:`device_node`: + + >>> os.stat(sda.device_node).st_rdev + 2048 + + .. versionadded:: 0.11 + """ + return self._libudev.udev_device_get_devnum(self) + + @property + def is_initialized(self): + """ + ``True``, if the device is initialized, ``False`` otherwise. + + A device is initialized, if udev has already handled this device and + has set up device node permissions and context, or renamed a network + device. + + Consequently, this property is only implemented for devices with a + device node or for network devices. On all other devices this property + is always ``True``. + + It is *not* recommended, that you use uninitialized devices. + + .. seealso:: :attr:`time_since_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + return bool(self._libudev.udev_device_get_is_initialized(self)) + + @property + def time_since_initialized(self): + """ + The time elapsed since initialization as :class:`~datetime.timedelta`. + + This property is only implemented on devices, which need to store + properties in the udev database. On all other devices this property is + simply zero :class:`~datetime.timedelta`. + + .. seealso:: :attr:`is_initialized` + + .. udevversion:: 165 + + .. versionadded:: 0.8 + """ + microseconds = self._libudev.udev_device_get_usec_since_initialized( + self) + return timedelta(microseconds=microseconds) + + @property + def device_links(self): + """ + An iterator, which yields the absolute paths (including the device + directory, see :attr:`Context.device_path`) of all symbolic links + pointing to the :attr:`device_node` of this device. The paths are + unicode strings. + + UDev can create symlinks to the original device node (see + :attr:`device_node`) inside the device directory. This is often + used to assign a constant, fixed device node to devices like + removeable media, which technically do not have a constant device + node, or to map a single device into multiple device hierarchies. + The property provides access to all such symbolic links, which were + created by UDev for this device. + + .. warning:: + + Links are not necessarily resolved by + :meth:`Devices.from_device_file()`. Hence do *not* rely on + ``Devices.from_device_file(context, link).device_path == + device.device_path`` from any ``link`` in ``device.device_links``. + """ + devlinks = self._libudev.udev_device_get_devlinks_list_entry(self) + for name, _ in udev_list_iterate(self._libudev, devlinks): + yield ensure_unicode_string(name) + + @property + def action(self): + """ + The device event action as string, or ``None``, if this device was not + received from a :class:`Monitor`. + + Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + .. warning:: + + Though the actions listed above are the most common, this property + *may* return other values, too, so be prepared to handle unknown + actions! + + .. versionadded:: 0.16 + """ + action = self._libudev.udev_device_get_action(self) + return ensure_unicode_string(action) if action is not None else None + + @property + def sequence_number(self): + """ + The device event sequence number as integer, or ``0`` if this device + has no sequence number, i.e. was not received from a :class:`Monitor`. + + .. versionadded:: 0.16 + """ + return self._libudev.udev_device_get_seqnum(self) + + @property + def attributes(self): + """ + The system attributes of this device as read-only + :class:`Attributes` mapping. + + System attributes are basically normal files inside the device + directory. These files contain all sorts of information about the + device, which may not be reflected by properties. These attributes + are commonly used for matching in udev rules, and can be printed + using ``udevadm info --attribute-walk``. + + The values of these attributes are not always proper strings, and + can contain arbitrary bytes. + + :returns: an Attributes object, useful for reading attributes + :rtype: Attributes + + .. versionadded:: 0.5 + """ + # do *not* cache the created object in an attribute of this class. + # Doing so creates an uncollectable reference cycle between Device and + # Attributes, because Attributes refers to this object through + # Attributes.device. + return Attributes(self) + + @property + def properties(self): + """ + The udev properties of this object as read-only Properties mapping. + + .. versionadded:: 0.21 + """ + return Properties(self) + + @property + def tags(self): + """ + A :class:`Tags` object representing the tags attached to this device. + + The :class:`Tags` object supports a test for a single tag as well as + iteration over all tags: + + >>> from pyudev import Context + >>> context = Context() + >>> device = next(iter(context.list_devices(tag='systemd'))) + >>> 'systemd' in device.tags + True + >>> list(device.tags) + [u'seat', u'systemd', u'uaccess'] + + Tags are arbitrary classifiers that can be attached to devices by udev + scripts and daemons. For instance, systemd_ uses tags for multi-seat_ + support. + + .. _systemd: http://freedesktop.org/wiki/Software/systemd + .. _multi-seat: http://www.freedesktop.org/wiki/Software/systemd/multiseat + + .. udevversion:: 154 + + .. versionadded:: 0.6 + + .. versionchanged:: 0.13 + Return a :class:`Tags` object now. + """ + return Tags(self) + + def __iter__(self): + """ + Iterate over the names of all properties defined for this device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__iter__() + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__len__() + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Access properties with Device.properties. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Access properties with Device.properties.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.__getitem__(prop) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asint() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asint instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asint(prop) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + + .. deprecated:: 0.21 + Will be removed in 1.0. Use Device.properties.asbool() instead. + """ + import warnings + warnings.warn( + 'Will be removed in 1.0. Use Device.properties.asbool instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.properties.asbool(prop) + + def __hash__(self): + return hash(self.device_path) + + def __eq__(self, other): + if isinstance(other, Device): + return self.device_path == other.device_path + else: + return self.device_path == other + + def __ne__(self, other): + if isinstance(other, Device): + return self.device_path != other.device_path + else: + return self.device_path != other + + def __gt__(self, other): + raise TypeError('Device not orderable') + + def __lt__(self, other): + raise TypeError('Device not orderable') + + def __le__(self, other): + raise TypeError('Device not orderable') + + def __ge__(self, other): + raise TypeError('Device not orderable') + + +class Properties(Mapping): + """ + udev properties :class:`Device` objects. + + .. versionadded:: 0.21 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def __iter__(self): + """ + Iterate over the names of all properties defined for the device. + + Return a generator yielding the names of all properties of this + device as unicode strings. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + for name, _ in udev_list_iterate(self._libudev, properties): + yield ensure_unicode_string(name) + + def __len__(self): + """ + Return the amount of properties defined for this device as integer. + """ + properties = \ + self._libudev.udev_device_get_properties_list_entry(self.device) + return sum(1 for _ in udev_list_iterate(self._libudev, properties)) + + def __getitem__(self, prop): + """ + Get the given property from this device. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as unicode string, or raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device. + """ + value = self._libudev.udev_device_get_property_value( + self.device, + ensure_byte_string(prop) + ) + if value is None: + raise KeyError(prop) + return ensure_unicode_string(value) + + def asint(self, prop): + """ + Get the given property from this device as integer. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return the property value as integer. Raise a + :exc:`~exceptions.KeyError`, if the given property is not defined + for this device, or a :exc:`~exceptions.ValueError`, if the property + value cannot be converted to an integer. + """ + return int(self[prop]) + + def asbool(self, prop): + """ + Get the given property from this device as boolean. + + A boolean property has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + + ``prop`` is a unicode or byte string containing the name of the + property. + + Return ``True``, if the property value is ``'1'`` and ``False``, if + the property value is ``'0'``. Any other value raises a + :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, + if the given property is not defined for this device. + """ + return string_to_bool(self[prop]) + + +class Attributes(object): + """ + udev attributes for :class:`Device` objects. + + .. versionadded:: 0.5 + """ + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + @property + def available_attributes(self): + """ + Yield the ``available`` attributes for the device. + + It is not guaranteed that a key in this list will have a value. + It is not guaranteed that a key not in this list will not have a value. + + It is guaranteed that the keys in this list are the keys that libudev + considers to be "available" attributes. + + If libudev version does not define udev_device_get_sysattr_list_entry() + yields nothing. + + See rhbz#1267584. + """ + if not hasattr(self._libudev, 'udev_device_get_sysattr_list_entry'): + return # pragma: no cover + attrs = self._libudev.udev_device_get_sysattr_list_entry(self.device) + for attribute, _ in udev_list_iterate(self._libudev, attrs): + yield ensure_unicode_string(attribute) + + def _get(self, attribute): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute`` + :rtype: an arbitrary sequence of bytes + :raises KeyError: if no value found + """ + value = self._libudev.udev_device_get_sysattr_value( + self.device, + ensure_byte_string(attribute) + ) + if value is None: + raise KeyError(attribute) + return value + + def get(self, attribute, default=None): + """ + Get the given system ``attribute`` for the device. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :param default: a default if no corresponding value found + :type default: a sequence of bytes + :returns: the value corresponding to ``attribute`` or ``default`` + :rtype: object + """ + try: + return self._get(attribute) + except KeyError: + return default + + def asstring(self, attribute): + """ + Get the given ``attribute`` for the device as unicode string. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as unicode + :rtype: unicode + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible + """ + return ensure_unicode_string(self._get(attribute)) + + def asint(self, attribute): + """ + Get the given ``attribute`` as an int. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as an int + :rtype: int + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to an int + """ + return int(self.asstring(attribute)) + + def asbool(self, attribute): + """ + Get the given ``attribute`` from this device as a bool. + + :param attribute: the key for an attribute value + :type attribute: unicode or byte string + :returns: the value corresponding to ``attribute``, as bool + :rtype: bool + :raises KeyError: if no value found for ``attribute`` + :raises UnicodeDecodeError: if value is not convertible to unicode + :raises ValueError: if unicode value can not be converted to a bool + + A boolean attribute has either a value of ``'1'`` or of ``'0'``, + where ``'1'`` stands for ``True``, and ``'0'`` for ``False``. Any + other value causes a :exc:`~exceptions.ValueError` to be raised. + """ + return string_to_bool(self.asstring(attribute)) + + +class Tags(Iterable, Container): + """ + A iterable over :class:`Device` tags. + + Subclasses the ``Container`` and the ``Iterable`` ABC. + """ + + # pylint: disable=too-few-public-methods + + def __init__(self, device): + self.device = device + self._libudev = device._libudev + + def _has_tag(self, tag): + """ + Whether ``tag`` exists. + + :param tag: unicode string with name of tag + :rtype: bool + """ + if hasattr(self._libudev, 'udev_device_has_tag'): + return bool(self._libudev.udev_device_has_tag( + self.device, ensure_byte_string(tag))) + else: # pragma: no cover + return any(t == tag for t in self) + + def __contains__(self, tag): + """ + Check for existence of ``tag``. + + ``tag`` is a tag as unicode string. + + Return ``True``, if ``tag`` is attached to the device, ``False`` + otherwise. + """ + return self._has_tag(tag) + + def __iter__(self): + """ + Iterate over all tags. + + Yield each tag as unicode string. + """ + tags = self._libudev.udev_device_get_tags_list_entry(self.device) + for tag, _ in udev_list_iterate(self._libudev, tags): + yield ensure_unicode_string(tag) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/discover.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/discover.py new file mode 100644 index 0000000..f2b4261 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/discover.py @@ -0,0 +1,391 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.discover + =============== + + Tools to discover a device given limited information. + + .. moduleauthor:: mulhern +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import abc +import functools +import os +import re +import six + +from ._errors import DeviceNotFoundError + +from .device import Devices + + +def wrap_exception(func): + """ + Allow Device discovery methods to return None instead of raising an + exception. + """ + + @functools.wraps(func) + def the_func(*args, **kwargs): + """ + Returns result of calling ``func`` on ``args``, ``kwargs``. + Returns None if ``func`` raises :exc:`DeviceNotFoundError`. + """ + try: + return func(*args, **kwargs) + except DeviceNotFoundError: + return None + + return the_func + +@six.add_metaclass(abc.ABCMeta) +class Hypothesis(object): + """ + Represents a hypothesis about the meaning of the device identifier. + """ + + @classmethod + @abc.abstractmethod + def match(cls, value): # pragma: no cover + """ + Match the given string according to the hypothesis. + + The purpose of this method is to obtain a value corresponding to + ``value`` if that is possible. It may use a regular expression, but + in general it should just return ``value`` and let the lookup method + sort out the rest. + + :param str value: the string to inspect + :returns: the matched thing or None if unmatched + :rtype: the type of lookup's key parameter or NoneType + """ + raise NotImplementedError() + + @classmethod + @abc.abstractmethod + def lookup(cls, context, key): # pragma: no cover + """ + Lookup the given string according to the hypothesis. + + :param Context context: the pyudev context + :param key: a key with which to lookup the device + :type key: the type of match's return value if not None + :returns: a list of Devices obtained + :rtype: frozenset of :class:`Device` + """ + raise NotImplementedError() + + @classmethod + def setup(cls, context): + """ + A potentially expensive method that may allow an :class:`Hypothesis` + to find devices more rapidly or to find a device that it would + otherwise miss. + + :param Context context: the pyudev context + """ + pass + + @classmethod + def get_devices(cls, context, value): + """ + Get any devices that may correspond to the given string. + + :param Context context: the pyudev context + :param str value: the value to look for + :returns: a list of devices obtained + :rtype: set of :class:`Device` + """ + key = cls.match(value) + return cls.lookup(context, key) if key is not None else frozenset() + + +class DeviceNumberHypothesis(Hypothesis): + """ + Represents the hypothesis that the device is a device number. + + The device may be separated into major/minor number or a composite number. + """ + + @classmethod + def _match_major_minor(cls, value): + """ + Match the number under the assumption that it is a major,minor pair. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + major_minor_re = re.compile( + r'^(?P\d+)(\D+)(?P\d+)$' + ) + match = major_minor_re.match(value) + return match and os.makedev( + int(match.group('major')), + int(match.group('minor')) + ) + + @classmethod + def _match_number(cls, value): + """ + Match the number under the assumption that it is a single number. + + :param str value: value to match + :returns: the device number or None + :rtype: int or NoneType + """ + number_re = re.compile(r'^(?P\d+)$') + match = number_re.match(value) + return match and int(match.group('number')) + + @classmethod + def match(cls, value): + """ + Match the number under the assumption that it is a device number. + + :returns: the device number or None + :rtype: int or NoneType + """ + return cls._match_major_minor(value) or cls._match_number(value) + + @classmethod + def find_subsystems(cls, context): + """ + Find subsystems in /sys/dev. + + :param Context context: the context + :returns: a lis of available subsystems + :rtype: list of str + """ + sys_path = context.sys_path + return os.listdir(os.path.join(sys_path, 'dev')) + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the device number. + + :param Context context: the context + :param int key: the device number + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_device_number) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DevicePathHypothesis(Hypothesis): + """ + Discover the device assuming the identifier is a device path. + """ + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device path. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + res = wrap_exception(Devices.from_path)(context, key) + return frozenset((res,)) if res is not None else frozenset() + + +class DeviceNameHypothesis(Hypothesis): + """ + Discover the device assuming the input is a device name. + + Try every available subsystem. + """ + + @classmethod + def find_subsystems(cls, context): + """ + Find all subsystems in sysfs. + + :param Context context: the context + :rtype: frozenset + :returns: subsystems in sysfs + """ + sys_path = context.sys_path + dirnames = ('bus', 'class', 'subsystem') + absnames = (os.path.join(sys_path, name) for name in dirnames) + realnames = (d for d in absnames if os.path.isdir(d)) + return frozenset(n for d in realnames for n in os.listdir(d)) + + @classmethod + def match(cls, value): + """ + Match ``value`` under the assumption that it is a device name. + + :returns: the device path or None + :rtype: str or NoneType + """ + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup by the path. + + :param Context context: the context + :param str key: the device path + :returns: a list of matching devices + :rtype: frozenset of :class:`Device` + """ + func = wrap_exception(Devices.from_name) + res = (func(context, s, key) for s in cls.find_subsystems(context)) + return frozenset(r for r in res if r is not None) + + +class DeviceFileHypothesis(Hypothesis): + """ + Discover the device assuming the value is some portion of a device file. + + The device file may be a link to a device node. + """ + + _LINK_DIRS = [ + '/dev', + '/dev/disk/by-id', + '/dev/disk/by-label', + '/dev/disk/by-partlabel', + '/dev/disk/by-partuuid', + '/dev/disk/by-path', + '/dev/disk/by-uuid', + '/dev/input/by-path', + '/dev/mapper', + '/dev/md', + '/dev/vg' + ] + + @classmethod + def get_link_dirs(cls, context): + """ + Get all directories that may contain links to device nodes. + + This method checks the device links of every device, so it is very + expensive. + + :param Context context: the context + :returns: a sorted list of directories that contain device links + :rtype: list + """ + devices = context.list_devices() + devices_with_links = (d for d in devices if list(d.device_links)) + links = (l for d in devices_with_links for l in d.device_links) + return sorted(set(os.path.dirname(l) for l in links)) + + @classmethod + def setup(cls, context): + """ + Set the link directories to be used when discovering by file. + + Uses `get_link_dirs`, so is as expensive as it is. + + :param Context context: the context + """ + cls._LINK_DIRS = cls.get_link_dirs(context) + + @classmethod + def match(cls, value): + return value + + @classmethod + def lookup(cls, context, key): + """ + Lookup the device under the assumption that the key is part of + the name of a device file. + + :param Context context: the context + :param str key: a portion of the device file name + + It is assumed that either it is the whole name of the device file + or it is the basename. + + A device file may be a device node or a device link. + """ + func = wrap_exception(Devices.from_device_file) + if '/' in key: + device = func(context, key) + return frozenset((device,)) if device is not None else frozenset() + else: + files = (os.path.join(ld, key) for ld in cls._LINK_DIRS) + devices = (func(context, f) for f in files) + return frozenset(d for d in devices if d is not None) + + +class Discovery(object): + # pylint: disable=too-few-public-methods + """ + Provides discovery methods for devices. + """ + + _HYPOTHESES = [ + DeviceFileHypothesis, + DeviceNameHypothesis, + DeviceNumberHypothesis, + DevicePathHypothesis + ] + + def __init__(self): + self._hypotheses = self._HYPOTHESES + + def setup(self, context): + """ + Set up individual hypotheses. + + May be an expensive call. + + :param Context context: the context + """ + for hyp in self._hypotheses: + hyp.setup(context) + + def get_devices(self, context, value): + """ + Get the devices corresponding to value. + + :param Context context: the context + :param str value: some identifier of the device + :returns: a list of corresponding devices + :rtype: frozenset of :class:`Device` + """ + return frozenset( + d for h in self._hypotheses for d in h.get_devices(context, value) + ) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/glib.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/glib.py new file mode 100644 index 0000000..4cb5c0e --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/glib.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""pyudev.glib + =========== + + Glib integration. + + :class:`MonitorObserver` integrates device monitoring into the Glib + mainloop by turing device events into Glib signals. + + :mod:`glib` and :mod:`gobject` from PyGObject_ must be available when + importing this module. PyGtk is not required. + + .. _PyGObject: http://www.pygtk.org/ + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.7 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +# thanks to absolute imports, this really imports the glib binding and not this +# module again +import glib +import gobject + + +class _ObserverMixin(object): + """Mixin to provide observer behavior to the old and the new API.""" + # pylint: disable=too-few-public-methods + + def _setup_observer(self, monitor): + # pylint: disable=attribute-defined-outside-init + self.monitor = monitor + self.event_source = None + self.enabled = True + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + + .. versionadded:: 0.14 + """ + return self.event_source is not None + + @enabled.setter + def enabled(self, value): + if value and self.event_source is None: + # pylint: disable=attribute-defined-outside-init + self.event_source = glib.io_add_watch( + self.monitor, glib.IO_IN, self._process_udev_event) + elif not value and self.event_source is not None: + glib.source_remove(self.event_source) + + def _process_udev_event(self, source, condition): + # pylint: disable=unused-argument + if condition == glib.IO_IN: + device = self.monitor.poll(timeout=0) + if device is not None: + self._emit_event(device) + return True + + def _emit_event(self, device): + self.emit('device-event', device) + + +class MonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + This class inherits :class:`~gobject.GObject` to turn device events into + glib signals. + + >>> from pyudev import Context, Monitor + >>> from pyudev.glib import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(observer, device): + ... print('event {0} on device {1}'.format(device.action, device)) + >>> observer.connect('device-event', device_event) + >>> monitor.start() + + This class is a child of :class:`gobject.GObject`. + """ + + __gsignals__ = { + # explicitly convert the signal to str, because glib expects the + # *native* string type of the corresponding python version as type of + # signal name, and str() is the name of the native string type of both + # python versions. We could also remove the "unicode_literals" import, + # but I don't want to make exceptions to the standard set of future + # imports used throughout pyudev for the sake of consistency. + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + + +gobject.type_register(MonitorObserver) + + +class GUDevMonitorObserver(gobject.GObject, _ObserverMixin): + """ + An observer for device events integrating into the :mod:`glib` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_signal_map = { + 'add': 'device-added', 'remove': 'device-removed', + 'change': 'device-changed', 'move': 'device-moved'} + + __gsignals__ = { + str('device-event'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)), + str('device-added'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-removed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-changed'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + str('device-moved'): (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + } + + def __init__(self, monitor): + gobject.GObject.__init__(self) + self._setup_observer(monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.glib.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + self.emit('device-event', device.action, device) + signal = self._action_signal_map.get(device.action) + if signal is not None: + self.emit(signal, device) + + +gobject.type_register(GUDevMonitorObserver) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/monitor.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/monitor.py new file mode 100644 index 0000000..ec3e7e4 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/monitor.py @@ -0,0 +1,582 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.monitor + ============== + + Monitor implementation. + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +import os +import errno +from threading import Thread +from functools import partial + +from .device import Device + +from ._util import eintr_retry_call +from ._util import ensure_byte_string + +from ._os import pipe +from ._os import poll + + +class Monitor(object): + """ + A synchronous device event monitor. + + A :class:`Monitor` objects connects to the udev daemon and listens for + changes to the device list. A monitor is created by connecting to the + kernel daemon through netlink (see :meth:`from_netlink`): + + >>> from pyudev import Context, Monitor + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + + Once the monitor is created, you can add a filter using :meth:`filter_by()` + or :meth:`filter_by_tag()` to drop incoming events in subsystems, which are + not of interest to the application: + + >>> monitor.filter_by('input') + + When the monitor is eventually set up, you can either poll for events + synchronously: + + >>> device = monitor.poll(timeout=3) + >>> if device: + ... print('{0.action}: {0}'.format(device)) + ... + + Or you can monitor events asynchronously with :class:`MonitorObserver`. + + To integrate into various event processing frameworks, the monitor provides + a :func:`selectable ` file description by :meth:`fileno()`. + However, do *not* read or write directly on this file descriptor. + + Instances of this class can directly be given as ``udev_monitor *`` to + functions wrapped through :mod:`ctypes`. + + .. versionchanged:: 0.16 + Remove :meth:`from_socket()` which is deprecated, and even removed in + recent udev versions. + """ + + def __init__(self, context, monitor_p): + self.context = context + self._as_parameter_ = monitor_p + self._libudev = context._libudev + self._started = False + + def __del__(self): + self._libudev.udev_monitor_unref(self) + + @classmethod + def from_netlink(cls, context, source='udev'): + """ + Create a monitor by connecting to the kernel daemon through netlink. + + ``context`` is the :class:`Context` to use. ``source`` is a string, + describing the event source. Two sources are available: + + ``'udev'`` (the default) + Events emitted after udev as registered and configured the device. + This is the absolutely recommended source for applications. + + ``'kernel'`` + Events emitted directly after the kernel has seen the device. The + device has not yet been configured by udev and might not be usable + at all. **Never** use this, unless you know what you are doing. + + Return a new :class:`Monitor` object, which is connected to the + given source. Raise :exc:`~exceptions.ValueError`, if an invalid + source has been specified. Raise + :exc:`~exceptions.EnvironmentError`, if the creation of the monitor + failed. + """ + if source not in ('kernel', 'udev'): + raise ValueError('Invalid source: {0!r}. Must be one of "udev" ' + 'or "kernel"'.format(source)) + monitor = context._libudev.udev_monitor_new_from_netlink( + context, ensure_byte_string(source)) + if not monitor: + raise EnvironmentError('Could not create udev monitor') + return cls(context, monitor) + + @property + def started(self): + """ + ``True``, if this monitor was started, ``False`` otherwise. Readonly. + + .. seealso:: :meth:`start()` + .. versionadded:: 0.16 + """ + return self._started + + def fileno(self): + # pylint: disable=anomalous-backslash-in-string + """ + Return the file description associated with this monitor as integer. + + This is really a real file descriptor ;), which can be watched and + :func:`select.select`\ ed. + """ + return self._libudev.udev_monitor_get_fd(self) + + def filter_by(self, subsystem, device_type=None): + """ + Filter incoming events. + + ``subsystem`` is a byte or unicode string with the name of a + subsystem (e.g. ``'input'``). Only events originating from the + given subsystem pass the filter and are handed to the caller. + + If given, ``device_type`` is a byte or unicode string specifying the + device type. Only devices with the given device type are propagated + to the caller. If ``device_type`` is not given, no additional + filter for a specific device type is installed. + + These filters are executed inside the kernel, and client processes + will usually not be woken up for device, that do not match these + filters. + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + subsystem = ensure_byte_string(subsystem) + if device_type is not None: + device_type = ensure_byte_string(device_type) + self._libudev.udev_monitor_filter_add_match_subsystem_devtype( + self, subsystem, device_type) + self._libudev.udev_monitor_filter_update(self) + + def filter_by_tag(self, tag): + """ + Filter incoming events by the given ``tag``. + + ``tag`` is a byte or unicode string with the name of a tag. Only + events for devices which have this tag attached pass the filter and are + handed to the caller. + + Like with :meth:`filter_by` this filter is also executed inside the + kernel, so that client processes are usually not woken up for devices + without the given ``tag``. + + .. udevversion:: 154 + + .. versionadded:: 0.9 + + .. versionchanged:: 0.15 + This method can also be after :meth:`start()` now. + """ + self._libudev.udev_monitor_filter_add_match_tag( + self, ensure_byte_string(tag)) + self._libudev.udev_monitor_filter_update(self) + + def remove_filter(self): + """ + Remove any filters installed with :meth:`filter_by()` or + :meth:`filter_by_tag()` from this monitor. + + .. warning:: + + Up to udev 181 (and possibly even later versions) the underlying + ``udev_monitor_filter_remove()`` seems to be broken. If used with + affected versions this method always raises + :exc:`~exceptions.ValueError`. + + Raise :exc:`~exceptions.EnvironmentError` if removal of installed + filters failed. + + .. versionadded:: 0.15 + """ + self._libudev.udev_monitor_filter_remove(self) + self._libudev.udev_monitor_filter_update(self) + + def enable_receiving(self): + """ + Switch the monitor into listing mode. + + Connect to the event source and receive incoming events. Only after + calling this method, the monitor listens for incoming events. + + .. note:: + + This method is implicitly called by :meth:`__iter__`. You don't + need to call it explicitly, if you are iterating over the + monitor. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`start()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.start() instead.', + DeprecationWarning) + self.start() + + def start(self): + """ + Start this monitor. + + The monitor will not receive events until this method is called. This + method does nothing if called on an already started :class:`Monitor`. + + .. note:: + + Typically you don't need to call this method. It is implicitly + called by :meth:`poll()` and :meth:`__iter__()`. + + .. seealso:: :attr:`started` + .. versionchanged:: 0.16 + This method does nothing if the :class:`Monitor` was already + started. + """ + if not self._started: + self._libudev.udev_monitor_enable_receiving(self) + # Force monitor FD into non-blocking mode + pipe.set_fd_status_flag(self, os.O_NONBLOCK) + self._started = True + + def set_receive_buffer_size(self, size): + """ + Set the receive buffer ``size``. + + ``size`` is the requested buffer size in bytes, as integer. + + .. note:: + + The CAP_NET_ADMIN capability must be contained in the effective + capability set of the caller for this method to succeed. Otherwise + :exc:`~exceptions.EnvironmentError` will be raised, with ``errno`` + set to :data:`~errno.EPERM`. Unprivileged processes typically lack + this capability. You can check the capabilities of the current + process with the python-prctl_ module: + + >>> import prctl + >>> prctl.cap_effective.net_admin + + Raise :exc:`~exceptions.EnvironmentError`, if the buffer size could not + bet set. + + .. versionadded:: 0.13 + + .. _python-prctl: http://packages.python.org/python-prctl + """ + self._libudev.udev_monitor_set_receive_buffer_size(self, size) + + def _receive_device(self): + """Receive a single device from the monitor. + + Return the received :class:`Device`, or ``None`` if no device could be + received. + + """ + while True: + try: + device_p = self._libudev.udev_monitor_receive_device(self) + return Device(self.context, device_p) if device_p else None + except EnvironmentError as error: + if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): + # No data available + return None + elif error.errno == errno.EINTR: + # Try again if our system call was interrupted + continue + else: + raise + + def poll(self, timeout=None): + """ + Poll for a device event. + + You can use this method together with :func:`iter()` to synchronously + monitor events in the current thread:: + + for device in iter(monitor.poll, None): + print('{0.action} on {0.device_path}'.format(device)) + + Since this method will never return ``None`` if no ``timeout`` is + specified, this is effectively an endless loop. With + :func:`functools.partial()` you can also create a loop that only waits + for a specified time:: + + for device in iter(partial(monitor.poll, 3), None): + print('{0.action} on {0.device_path}'.format(device)) + + This loop will only wait three seconds for a new device event. If no + device event occurred after three seconds, the loop will exit. + + ``timeout`` is a floating point number that specifies a time-out in + seconds. If omitted or ``None``, this method blocks until a device + event is available. If ``0``, this method just polls and will never + block. + + .. note:: + + This method implicitly calls :meth:`start()`. + + Return the received :class:`Device`, or ``None`` if a timeout + occurred. Raise :exc:`~exceptions.EnvironmentError` if event retrieval + failed. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.16 + """ + if timeout is not None and timeout > 0: + # .poll() takes timeout in milliseconds + timeout = int(timeout * 1000) + self.start() + if eintr_retry_call(poll.Poll.for_events((self, 'r')).poll, timeout): + return self._receive_device() + else: + return None + + def receive_device(self): + """ + Receive a single device from the monitor. + + .. warning:: + + You *must* call :meth:`start()` before calling this method. + + The caller must make sure, that there are events available in the + event queue. The call blocks, until a device is available. + + If a device was available, return ``(action, device)``. ``device`` + is the :class:`Device` object describing the device. ``action`` is + a string describing the action. Usual actions are: + + ``'add'`` + A device has been added (e.g. a USB device was plugged in) + ``'remove'`` + A device has been removed (e.g. a USB device was unplugged) + ``'change'`` + Something about the device changed (e.g. a device property) + ``'online'`` + The device is online now + ``'offline'`` + The device is offline now + + Raise :exc:`~exceptions.EnvironmentError`, if no device could be + read. + + .. deprecated:: 0.16 + Will be removed in 1.0. Use :meth:`Monitor.poll()` instead. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use Monitor.poll() instead.', + DeprecationWarning) + device = self.poll() + return device.action, device + + def __iter__(self): + """ + Wait for incoming events and receive them upon arrival. + + This methods implicitly calls :meth:`start()`, and starts polling the + :meth:`fileno` of this monitor. If a event comes in, it receives the + corresponding device and yields it to the caller. + + The returned iterator is endless, and continues receiving devices + without ever stopping. + + Yields ``(action, device)`` (see :meth:`receive_device` for a + description). + + .. deprecated:: 0.16 + Will be removed in 1.0. Use an explicit loop over :meth:`poll()` + instead, or monitor asynchronously with :class:`MonitorObserver`. + """ + import warnings + warnings.warn('Will be removed in 1.0. Use an explicit loop over ' + '"poll()" instead, or monitor asynchronously with ' + '"MonitorObserver".', DeprecationWarning) + self.start() + while True: + device = self.poll() + if device is not None: + yield device.action, device + + +class MonitorObserver(Thread): + """ + An asynchronous observer for device events. + + This class subclasses :class:`~threading.Thread` class to asynchronously + observe a :class:`Monitor` in a background thread: + + >>> from pyudev import Context, Monitor, MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> def print_device_event(device): + ... print('background event {0.action}: {0.device_path}'.format(device)) + >>> observer = MonitorObserver(monitor, callback=print_device_event, name='monitor-observer') + >>> observer.daemon + True + >>> observer.start() + + In the above example, input device events will be printed in background, + until :meth:`stop()` is called on ``observer``. + + .. note:: + + Instances of this class are always created as daemon thread. If you do + not want to use daemon threads for monitoring, you need explicitly set + :attr:`~threading.Thread.daemon` to ``False`` before invoking + :meth:`~threading.Thread.start()`. + + .. seealso:: + + :attr:`Device.action` + The action that created this event. + + :attr:`Device.sequence_number` + The sequence number of this event. + + .. versionadded:: 0.14 + + .. versionchanged:: 0.15 + :meth:`Monitor.start()` is implicitly called when the thread is started. + """ + + def __init__(self, monitor, event_handler=None, callback=None, *args, + **kwargs): + """ + Create a new observer for the given ``monitor``. + + ``monitor`` is the :class:`Monitor` to observe. ``callback`` is the + callable to invoke on events, with the signature ``callback(device)`` + where ``device`` is the :class:`Device` that caused the event. + + .. warning:: + + ``callback`` is invoked in the observer thread, hence the observer + is blocked while callback executes. + + ``args`` and ``kwargs`` are passed unchanged to the constructor of + :class:`~threading.Thread`. + + .. deprecated:: 0.16 + The ``event_handler`` argument will be removed in 1.0. Use + the ``callback`` argument instead. + .. versionchanged:: 0.16 + Add ``callback`` argument. + """ + if callback is None and event_handler is None: + raise ValueError('callback missing') + elif callback is not None and event_handler is not None: + raise ValueError('Use either callback or event handler') + + Thread.__init__(self, *args, **kwargs) + self.monitor = monitor + # observer threads should not keep the interpreter alive + self.daemon = True + self._stop_event = None + if event_handler is not None: + import warnings + warnings.warn('"event_handler" argument will be removed in 1.0. ' + 'Use Monitor.poll() instead.', DeprecationWarning) + callback = lambda d: event_handler(d.action, d) + self._callback = callback + + def start(self): + """Start the observer thread.""" + if not self.is_alive(): + self._stop_event = pipe.Pipe.open() + Thread.start(self) + + def run(self): + self.monitor.start() + notifier = poll.Poll.for_events( + (self.monitor, 'r'), (self._stop_event.source, 'r')) + while True: + for file_descriptor, event in eintr_retry_call(notifier.poll): + if file_descriptor == self._stop_event.source.fileno(): + # in case of a stop event, close our pipe side, and + # return from the thread + self._stop_event.source.close() + return + elif file_descriptor == self.monitor.fileno() and event == 'r': + read_device = partial(eintr_retry_call, self.monitor.poll, timeout=0) + for device in iter(read_device, None): + self._callback(device) + else: + raise EnvironmentError('Observed monitor hung up') + + def send_stop(self): + """ + Send a stop signal to the background thread. + + The background thread will eventually exit, but it may still be running + when this method returns. This method is essentially the asynchronous + equivalent to :meth:`stop()`. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + """ + if self._stop_event is None: + return + with self._stop_event.sink: + # emit a stop event to the thread + eintr_retry_call(self._stop_event.sink.write, b'\x01') + self._stop_event.sink.flush() + + def stop(self): + """ + Synchronously stop the background thread. + + .. note:: + + This method can safely be called from the observer thread. In this + case it is equivalent to :meth:`send_stop()`. + + Send a stop signal to the backgroud (see :meth:`send_stop`), and waits + for the background thread to exit (see :meth:`~threading.Thread.join`) + if the current thread is *not* the observer thread. + + After this method returns in a thread *that is not the observer + thread*, the ``callback`` is guaranteed to not be invoked again + anymore. + + .. note:: + + The underlying :attr:`monitor` is *not* stopped. + + .. versionchanged:: 0.16 + This method can be called from the observer thread. + """ + self.send_stop() + try: + self.join() + except RuntimeError: + pass diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt4.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt4.py new file mode 100644 index 0000000..dc1d56b --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt4.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyqt4 + ============ + + PyQt4 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt4\_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt4.QtCore` from PyQt4\_ must be available when importing this + module. + + .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Sebastian Wiesner +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt4 import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) + + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt5.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt5.py new file mode 100644 index 0000000..73ab057 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyqt5.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" + pyudev.pyqt5 + ============ + + PyQt5 integration. + + :class:`MonitorObserver` integrates device monitoring into the PyQt5_ + mainloop by turning device events into Qt signals. + + :mod:`PyQt5.QtCore` from PyQt5_ must be available when importing this + module. + + .. _gPyQt5: http://riverbankcomputing.co.uk/software/pyqt/intro + + .. moduleauthor:: Tobias Gehring +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PyQt5 import QtCore + +from ._qt_base import MonitorObserverGenerator + + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.pyqtSignal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyside.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyside.py new file mode 100644 index 0000000..b090fa8 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/pyside.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +""" + pyudev.pyside + ============= + + PySide integration. + + :class:`QUDevMonitorObserver` integrates device monitoring into the PySide\_ + mainloop by turing device events into Qt signals. + + :mod:`PySide.QtCore` from PySide\_ must be available when importing this + module. + + .. _PySide: http://www.pyside.org + + .. moduleauthor:: Sebastian Wiesner + .. versionadded:: 0.6 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PySide import QtCore + +from ._qt_base import MonitorObserverGenerator +from ._qt_base import QUDevMonitorObserverGenerator + +# pylint: disable=invalid-name +MonitorObserver = MonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) + +""" +.. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. +""" +QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer( + QtCore.QObject, + QtCore.Signal, + QtCore.QSocketNotifier +) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/version.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/version.py new file mode 100644 index 0000000..43c1e96 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/version.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2015 mulhern + +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + pyudev.version + ============== + + Version information. + + .. moduleauthor:: mulhern +""" + +__version_info__ = (0, 21, 0, '') +__version__ = "%s%s" % \ + ( + ".".join(str(x) for x in __version_info__[:3]), + "".join(str(x) for x in __version_info__[3:]) + ) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/wx.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/wx.py new file mode 100644 index 0000000..8279dd4 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/pyudev/wx.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# Free Software Foundation; either version 2.1 of the License, or (at your +# option) any later version. + +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# pylint: disable=anomalous-backslash-in-string + +"""pyudev.wx + ========= + + Wx integration. + + :class:`MonitorObserver` integrates device monitoring into the wxPython\_ + mainloop by turing device events into wx events. + + :mod:`wx` from wxPython\_ must be available when importing this module. + + .. _wxPython: http://wxpython.org/ + + .. moduleauthor:: Tobias Eberle + .. versionadded:: 0.14 + +""" + + +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +from wx import EvtHandler, PostEvent +from wx.lib.newevent import NewEvent + +import pyudev + + +DeviceEvent, EVT_DEVICE_EVENT = NewEvent() + + +class MonitorObserver(EvtHandler): + """ + An observer for device events integrating into the :mod:`wx` mainloop. + + This class inherits :class:`~wx.EvtHandler` to turn device events into + wx events: + + >>> from pyudev import Context, Monitor + >>> from pyudev.wx import MonitorObserver + >>> context = Context() + >>> monitor = Monitor.from_netlink(context) + >>> monitor.filter_by(subsystem='input') + >>> observer = MonitorObserver(monitor) + >>> def device_event(event): + ... print('action {0} on device {1}'.format(event.device.action, event.device)) + >>> observer.Bind(EVT_DEVICE_EVENT, device_event) + >>> monitor.start() + + This class is a child of :class:`wx.EvtHandler`. + + .. versionadded:: 0.17 + """ + + def __init__(self, monitor): + EvtHandler.__init__(self) + self.monitor = monitor + self._observer_thread = None + self.start() + + @property + def enabled(self): + """ + Whether this observer is enabled or not. + + If ``True`` (the default), this observer is enabled, and emits events. + Otherwise it is disabled and does not emit any events. + """ + return self._observer_thread is not None + + @enabled.setter + def enabled(self, value): + if value: + self.start() + else: + self.stop() + + def start(self): + """ + Enable this observer. + + Do nothing, if the observer is already enabled. + """ + if self._observer_thread is not None: + return + self._observer_thread = pyudev.MonitorObserver( + self.monitor, callback=self._emit_event, + name='wx-observer-thread') + self._observer_thread.start() + + def stop(self): + """ + Disable this observer. + + Do nothing, if the observer is already disabled. + """ + if self._observer_thread is None: + return + self._observer_thread.stop() + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(device=device)) + + +DeviceAddedEvent, EVT_DEVICE_ADDED = NewEvent() +DeviceRemovedEvent, EVT_DEVICE_REMOVED = NewEvent() +DeviceChangedEvent, EVT_DEVICE_CHANGED = NewEvent() +DeviceMovedEvent, EVT_DEVICE_MOVED = NewEvent() + + +class WxUDevMonitorObserver(MonitorObserver): + """An observer for device events integrating into the :mod:`wx` mainloop. + + .. deprecated:: 0.17 + Will be removed in 1.0. Use :class:`MonitorObserver` instead. + """ + + _action_event_map = { + 'add': DeviceAddedEvent, + 'remove': DeviceRemovedEvent, + 'change': DeviceChangedEvent, + 'move': DeviceMovedEvent + } + + def __init__(self, monitor): + MonitorObserver.__init__(self, monitor) + import warnings + warnings.warn('Will be removed in 1.0. ' + 'Use pyudev.wx.MonitorObserver instead.', + DeprecationWarning) + + def _emit_event(self, device): + PostEvent(self, DeviceEvent(action=device.action, device=device)) + event_class = self._action_event_map.get(device.action) + if event_class is not None: + PostEvent(self, event_class(device=device)) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/qemu.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/qemu.py new file mode 100644 index 0000000..467ba75 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/qemu.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: qemu.py +# Purpose: Module to boot ISO and USB disks using QEMU. +# Depends: QEMU must be installed under Linux for availing this feature. For windows, QEMU package is shipped +# along with executable file +# 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 .admin import adminCmd +from PyQt5 import QtWidgets +from .gui.ui_multibootusb import Ui_Dialog +from .gen import * + + +class Qemu(QtWidgets.QDialog, Ui_Dialog): + """ + ISO and USB booting using QEMU. + """ + def on_Qemu_Browse_iso_Click(self): + """ + Browse and choose an ISO. + :return: + """ + self.ui.lineEdit_2.clear() + + qemu = self.check_qemu_exist() + + if not qemu is None: + + qemu_iso_link = QtWidgets.QFileDialog.getOpenFileName(self, 'Select an iso...', "", "ISO Files (*.iso)")[0] + else: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu package to avail this feature.') + qemu_iso_link = None + + if not qemu_iso_link is None: + self.ui.lineEdit_2.insert(qemu_iso_link) + else: + log ("File not selected.") + + def on_Qemu_Boot_iso_Click(self): + """ + Main function to boot a selected ISO. + :return: + """ + if not self.ui.lineEdit_2.text(): + QtWidgets.QMessageBox.information(self, 'No ISO...', 'No ISO selected.\n\nPlease choose an iso and click Boot ISO.') + else: + qemu = self.check_qemu_exist() + qemu_iso_link = str(self.ui.lineEdit_2.text()) + if qemu is None: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_iso_ram() + if not ram is None: + self.ui.lineEdit_2.clear() + if platform.system() == "Windows": + try: + log("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Unable to start QEMU.') + else: + log(qemu + ' -m ' + ram + ' -cdrom ' + str(qemu_iso_link) + ' -boot d') + try: + log("Executing ==> " + qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram) + subprocess.Popen(qemu + " -cdrom " + str(qemu_iso_link) + " -boot d -m " + ram, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting ISO\n' + 'Unable to start QEMU.') + else: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot ISO.') + + + def on_Qemu_Boot_usb_Click(self, usb_disk): + """ + Main function to boot a selected USB disk. + :param usb_disk: Path to usb disk. + :return: + """ + qemu = self.check_qemu_exist() + + if qemu is None: + log("QEMU does not exist.\nPlease install qemu package to avail this feature.") + QtWidgets.QMessageBox.information(self, 'No QEMU...', 'Please install qemu to avail this feature.') + else: + ram = self.qemu_usb_ram() + if ram is None: + QtWidgets.QMessageBox.information(self, 'No ram...', 'No ram selected.\n\nPlease choose any ram value and click Boot USB.') + else: + if platform.system() == "Windows": + disk_number = self.get_physical_disk_number(usb_disk) + parent_dir = os.getcwd() + os.chdir(resource_path(os.path.join("data", "tools", "qemu"))) + try: + log("Executing ==> " + qemu + " -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number) + subprocess.Popen("qemu-system-x86_64.exe -L . -boot c -m " + ram + " -hda //./PhysicalDrive" + disk_number, shell=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n' + 'Unable to start QEMU.') + os.chdir(parent_dir) + elif platform.system() == "Linux": + try: + qemu_cmd = qemu + ' -hda ' + usb_disk[:-1] + ' -m ' + ram + ' -vga std' + log('Executing ==> ' + qemu_cmd) + # adminCmd([qemu, '-hda', usb_disk[:-1], '-m', ram, '-vga std'], gui=True) + subprocess.Popen(qemu_cmd, shell=True) + # adminCmd(qemu_cmd, gui=True) + except: + QtWidgets.QMessageBox.information(self, 'Error...', 'Error booting USB\n\nUnable to start QEMU.') + + def qemu_iso_ram(self): + """ + Choose a ram size for ISO booting. + :return: Ram size as string. + """ + if self.ui.ram_iso_256.isChecked(): + return str(256) + elif self.ui.ram_iso_512.isChecked(): + return str(512) + elif self.ui.ram_iso_768.isChecked(): + return str(768) + elif self.ui.ram_iso_1024.isChecked(): + return str(1024) + elif self.ui.ram_iso_2048.isChecked(): + return str(2047) + else: + return None + + def qemu_usb_ram(self): + """ + Choose a ram size for USB booting. + :return: Ram size as string. + """ + if self.ui.ram_usb_256.isChecked(): + return str(256) + if self.ui.ram_usb_512.isChecked(): + return str(512) + if self.ui.ram_usb_768.isChecked(): + return str(768) + if self.ui.ram_usb_1024.isChecked(): + return str(1024) + if self.ui.ram_usb_2048.isChecked(): + return str(2047) + else: + return None + + def check_qemu_exist(self): + """ + Check if QEMU is available on host system. + :return: path to QEMU program or None otherwise. + """ + if platform.system() == "Linux": + if subprocess.call('which qemu-system-x86_64', shell=True) == 0: + log("qemu-system-x86_64 exists...") + qemu = "qemu-system-x86_64" + elif subprocess.call('which qemu', shell=True) == 0: + log("qemu exists") + qemu = "qemu" + else: + qemu = None + + if qemu: + return qemu + else: + return None + elif platform.system() == "Windows": + log(resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe"))) + return resource_path(os.path.join("data", "tools", "qemu", "qemu-system-x86_64.exe")) + + + def get_physical_disk_number(self, usb_disk): + """ + Get the physical disk number as detected ny Windows. + :param usb_disk: USB disk (Like F:) + :return: Disk number. + """ + import wmi + c = wmi.WMI () + for physical_disk in c.Win32_DiskDrive (): + for partition in physical_disk.associators ("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators ("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == usb_disk: + """ + log physical_disk.Caption + log partition.Caption + log logical_disk.Caption + """ + log("Physical Device Number is " + partition.Caption[6:-14]) + return str(partition.Caption[6:-14]) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/syslinux.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/syslinux.py new file mode 100644 index 0000000..ff98fb1 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/syslinux.py @@ -0,0 +1,189 @@ +#!/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 sys +import subprocess +import platform +from .gen import * +from . import usb +from .iso import * +from . import config + +extlinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "extlinux4") +syslinux_path = os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4") +extlinux_fs = ["ext2", "ext3", "ext4", "Btrfs"] +syslinux_fs = ["vfat", "ntfs", "FAT32", "NTFS"] +mbr_bin = resource_path(os.path.join("data", "tools", "mbr.bin")) + + +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 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 + + +def syslinux_default(usb_disk, version=4): + """ + 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_install_cmd = 'dd bs=440 count=1 conv=notrunc if=' + mbr_bin + ' of=' + usb_disk[:-1] + # log (usb_fs) + 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) + if subprocess.call(extlinu_cmd, shell=True) == 0: + log ("\nDefault Extlinux install is success...\n") + log ('\nExecuting ==> ' + mbr_install_cmd) + if subprocess.call(mbr_install_cmd, shell=True) == 0: + log ("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + + elif usb_fs in syslinux_fs: + + if platform.system() == "Linux": + 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 ==> " + syslinux_cmd + "\n") + if subprocess.call(syslinux_cmd, shell=True) == 0: + log ("\nDefault syslinux install is success...\n") + if subprocess.call(mbr_install_cmd, shell=True) == 0: + log ("\nmbr install is success...\n") + if set_boot_flag(usb_disk) is True: + return True + else: + log ("\nFailed to install default syslinux...\n") + return False + + elif platform.system() == "Windows": + syslinux = resource_path(os.path.join(multibootusb_host_dir(), "syslinux", "bin", "syslinux4.exe")) + log ('Executing ==>' + syslinux + ' -maf -d multibootusb ' + usb_disk) + if subprocess.call(syslinux + ' -maf -d multibootusb ' + usb_disk, shell=True) == 0: + log ("\nDefault syslinux install is success...\n") + return True + else: + log ("\nFailed to install default syslinux...\n") + return False + + +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 distro == "generic" or distro == "alpine": + 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: + if config.syslinux_version == str(3): + if distro == "generic" and iso_linux_bin_dir == "/": + option = "" + else: + option = " -d " + else: + if distro == "generic" and iso_linux_bin_dir == "/": + option = " -i " + else: + option = " -i -d " + + if platform.system() == "Linux": + 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) == 0 + sys_cmd = syslinux_path + option + quote(distro_syslinux_install_dir) + ' ' + usb_disk + dd_cmd = 'dd if=' + usb_disk + ' ' + 'of=' + quote(distro_sys_install_bs) + ' count=1' + log ("Executing ==> " + sys_cmd) + if subprocess.call(sys_cmd, shell=True) == 0: + log ("\nSyslinux install on distro directory is success...\n") + log ('Executing ==> ' + dd_cmd + '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + log ("\nBootsector copy is success...\n") + else: + log ("\nFailed to copy boot sector...\n") + else: + log ("\nFailed to install syslinux on distro directory...\n") + 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 + option + distro_syslinux_install_dir + ' ' + usb_disk + ' ' + \ + distro_sys_install_bs + log ("\nExecuting ==> " + sys_cmd, '\n') + if subprocess.call(sys_cmd, shell=True) == 0: + log ("\nSyslinux install was successful on distro directory...\n") + else: + 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=' + usb_mount + 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) == 0 + log ("Executing ==> " + ext_cmd) + if subprocess.call(ext_cmd, shell=True) == 0: + log ("\nSyslinux install on distro directory is success...\n") + log ('Executing ==> ' + dd_cmd, '\n') + if subprocess.call(dd_cmd, shell=True) == 0: + log ("\nBootsector copy is success...\n") + else: + log ("\nFailed to install syslinux on distro directory...\n") + +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') diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/udisks.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/udisks.py new file mode 100644 index 0000000..659b62d --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/udisks.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +# Name: udisks.py +# Purpose: Module to mount unmount and eject using dbus and udisk +# Authors: Original author is Kovid Goyal and python3 +# supporte by Sundar for multibootusb project +# Licence: 'GPL v3' as per original Licence + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +# from __future__ import print_function +import os, re + + +def node_mountpoint(node): + + def de_mangle(raw): + return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012', + '\n').replace('\\0134', '\\') + + for line in open('/proc/mounts').readlines(): + line = line.split() + if line[0] == node: + return de_mangle(line[1]) + return None + + +class NoUDisks1(Exception): + pass + + +class UDisks(object): + + def __init__(self): + import dbus + self.bus = dbus.SystemBus() + try: + self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + '/org/freedesktop/UDisks'), 'org.freedesktop.UDisks') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown': + raise NoUDisks1() + raise + + def device(self, device_node_path): + import dbus + devpath = self.main.FindDeviceByDeviceFile(device_node_path) + return dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + devpath), 'org.freedesktop.UDisks.Device') + + def mount(self, device_node_path): + d = self.device(device_node_path) + try: + return str(d.FilesystemMount('', + ['auth_no_user_interaction', 'rw', 'noexec', 'nosuid', + 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()])) + except: + # May be already mounted, check + mp = node_mountpoint(str(device_node_path)) + if mp is None: + raise + return mp + + def unmount(self, device_node_path): + d = self.device(device_node_path) + d.FilesystemUnmount(['force']) + + def eject(self, device_node_path): + parent = device_node_path + while parent[-1] in '0123456789': + parent = parent[:-1] + d = self.device(parent) + d.DriveEject([]) + + +class NoUDisks2(Exception): + pass + + +class UDisks2(object): + + BLOCK = 'org.freedesktop.UDisks2.Block' + FILESYSTEM = 'org.freedesktop.UDisks2.Filesystem' + DRIVE = 'org.freedesktop.UDisks2.Drive' + + def __init__(self): + import dbus + self.bus = dbus.SystemBus() + try: + self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown': + raise NoUDisks2() + raise + + def device(self, device_node_path): + device_node_path = os.path.realpath(device_node_path) + devname = device_node_path.split('/')[-1] + + # First we try a direct object path + bd = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices/%s'%devname) + try: + device = bd.Get(self.BLOCK, 'Device', + dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + except: + device = None + + if device == device_node_path: + return bd + + # Enumerate all devices known to UDisks + devs = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices') + xml = devs.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable') + for dev in re.finditer(r'name=[\'"](.+?)[\'"]', type('')(xml)): + bd = self.bus.get_object('org.freedesktop.UDisks2', + '/org/freedesktop/UDisks2/block_devices/%s2'%dev.group(1)) + try: + device = bd.Get(self.BLOCK, 'Device', + dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + except: + device = None + if device == device_node_path: + return bd + + raise ValueError('%r not known to UDisks2'%device_node_path) + + def mount(self, device_node_path): + d = self.device(device_node_path) + mount_options = ['rw', 'noexec', 'nosuid', + 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()] + try: + return str(d.Mount( + { + 'auth.no_user_interaction':True, + 'options':','.join(mount_options) + }, + dbus_interface=self.FILESYSTEM)) + except: + # May be already mounted, check + mp = node_mountpoint(str(device_node_path)) + if mp is None: + raise + return mp + + def unmount(self, device_node_path): + d = self.device(device_node_path) + d.Unmount({'force':True, 'auth.no_user_interaction':True}, + dbus_interface=self.FILESYSTEM) + + def drive_for_device(self, device): + drive = device.Get(self.BLOCK, 'Drive', + dbus_interface='org.freedesktop.DBus.Properties') + return self.bus.get_object('org.freedesktop.UDisks2', drive) + + def eject(self, device_node_path): + drive = self.drive_for_device(self.device(device_node_path)) + drive.Eject({'auth.no_user_interaction':True}, + dbus_interface=self.DRIVE) + + +def get_udisks(ver=None): + if ver is None: + try: + u = UDisks2() + except NoUDisks2: + u = UDisks() + return u + return UDisks2() if ver == 2 else UDisks() + + +def get_udisks1(): + u = None + try: + u = UDisks() + except NoUDisks1: + try: + u = UDisks2() + except NoUDisks2: + pass + if u is None: + raise EnvironmentError('UDisks not available on your system') + return u + + +def mount(node_path): + u = get_udisks1() + u.mount(node_path) + + +def eject(node_path): + u = get_udisks1() + u.eject(node_path) + + +def umount(node_path): + u = get_udisks1() + u.unmount(node_path) + + +def test_udisks(ver=None): + import sys + dev = sys.argv[1] + print('Testing with node', dev) + u = get_udisks(ver=ver) + print('Using Udisks:', u.__class__.__name__) + print('Mounted at:', u.mount(dev)) + print('Unmounting') + u.unmount(dev) + print('Mounting') + u.mount(dev) + print('Ejecting:') + u.eject(dev) + +if __name__ == '__main__': + print('Run test here...') + # test_udisks() diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/uninstall_distro.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/uninstall_distro.py new file mode 100644 index 0000000..2dcf9fe --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/uninstall_distro.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: uninstall_distro.py +# Purpose: Module to uninstall distros installed by multibootusb +# 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 re +import shutil +import threading +import platform +from .usb import * +from . import config +from . import gen + + +def install_distro_list(): + """ + List all distro names installed by previous install + :return: List of distro names as list + """ + usb_details = details(config.usb_disk) + config.usb_mount = usb_details['mount_point'] + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + + if os.path.exists(sys_cfg_file): + distro_list = [] + for line in open(sys_cfg_file): + if "#start " in line: + distro_list.append(line[7:]) + return distro_list + else: + return None + + +def unin_distro(): + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + config.uninstall_distro_dir_name = config.uninstall_distro_dir_name.replace('\n', '') + gen.log(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + if config.distro: + uninstall_distro() + else: + return "" + + +def delete_frm_file_list(): + """ + Generic way to remove files from USB disk. + :param config.usb_disk: + :param iso_file_list: List of files installed in the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if config.iso_file_list is not None: + for f in config.iso_file_list: + if platform.system() == "Windows": + f = f.replace('\n', '').strip("/").replace("/", "\\") + else: + f = f.replace('\n', '').strip("/") + if os.path.exists(os.path.join(usb_mount, "ldlinux.sys")): + os.chmod(os.path.join(usb_mount, "ldlinux.sys"), 0o777) + os.unlink(os.path.join(usb_mount, "ldlinux.sys")) + + if os.path.exists(os.path.join(usb_mount, f)): + gen.log("Removing " + (os.path.join(usb_mount, f))) + if os.path.isfile(os.path.join(usb_mount, f)): + os.remove(os.path.join(usb_mount, f)) + elif os.path.isdir(os.path.join(usb_mount, f)): + shutil.rmtree(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "generic.cfg"), "r") as generic_cfg: + if platform.system() == "Windows": + generic = generic_cfg.read().replace('\n', '').replace("/", "\\") + else: + generic = generic_cfg.read().replace('\n', '') + if os.path.exists(os.path.join(usb_mount, generic.strip("/"))): + os.remove(os.path.join(usb_mount, generic.strip("/"))) + if platform.system() == 'Linux': + gen.log('Removed files from' + config.uninstall_distro_dir_name) + gen.log('Syncing....') + os.system('sync') + + + + +def uninstall_distro(): + """ + Uninstall selected distro from selected USB disk. + :param config.usb_disk: Path of the USB disk + :param config.uninstall_distro_dir_name: Directory where the distro is installed + :param _distro: Generic name applied to distro install by multibootusb + :return: + """ + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "iso_file_list.cfg"), "r") as f: + config.iso_file_list = f.readlines() + # gen.log iso_file_list + + for path, subdirs, files in os.walk(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + for name in files: + if name.endswith('ldlinux.sys') or name.endswith('ldlinux.c32'): + os.chmod(os.path.join(path, name), 0o777) + os.unlink(os.path.join(path, name)) + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")): + os.remove(os.path.join(usb_mount, config.uninstall_distro_dir_name + ".iso")) + elif config.distro == "windows" or config.distro == "alpine" or config.distro == "generic": + delete_frm_file_list() + if config.distro == "ipfire": + files = os.listdir(usb_mount) + for f in files: + if f.endswith('.tlz'): + os.remove(os.path.join(usb_mount, f)) + if os.path.exists(os.path.join(usb_mount, "distro.img")): + os.remove(os.path.join(usb_mount, "distro.img")) + elif config.distro == "trinity-rescue": + shutil.rmtree(os.path.join(usb_mount, "trk3")) + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)): + if platform.system() == 'Linux': + os.system('sync') + shutil.rmtree(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)) + + delete_frm_file_list() + + update_sys_cfg_file() + + +def update_sys_cfg_file(): + """ + Main function to remove uninstall distro specific operations. + :return: + """ + if platform.system() == 'Linux': + os.system('sync') + + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + if not os.path.exists(sys_cfg_file): + gen.log("syslinux.cfg file not found for updating changes.") + else: + gen.log("Updating syslinux.cfg file...") + string = open(sys_cfg_file).read() + string = re.sub(r'#start ' + config.uninstall_distro_dir_name + '.*?' + '#end ' + config.uninstall_distro_dir_name + '\s*', '', string, flags=re.DOTALL) + config_file = open(sys_cfg_file, "w") + config_file.write(string) + config_file.close() + + +def uninstall_progress(): + """ + Calculate uninstall progress percentage. + :return: + """ + from . import progressbar + usb_details = details(config.usb_disk) + usb_mount = usb_details['mount_point'] + if platform.system() == 'Linux': + os.system('sync') + + if os.path.exists(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg")): + with open(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name, "multibootusb.cfg"), + "r") as multibootusb_cfg: + config.distro = multibootusb_cfg.read().replace('\n', '') + else: + config.distro = "" + gen.log("Installed distro type is " + config.distro) + + if config.distro == "opensuse": + if os.path.exists(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso"): + folder_size_to_remove = os.path.getsize(os.path.join(usb_mount, config.uninstall_distro_dir_name) + ".iso") + else: + folder_size_to_remove = 0 + folder_size_to_remove += disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + elif config.distro == "windows" or config.distro == "Windows": + if os.path.exists(os.path.join(usb_mount, "SOURCES")): + folder_size_to_remove = disk_usage(str(usb_mount) + "/SOURCES").used + else: + folder_size_to_remove = disk_usage(str(usb_mount) + "/SSTR").used + elif config.distro == "ipfire": + folder_size_to_remove = disk_usage(str(usb_mount) + "/multibootusb/" + config.uninstall_distro_dir_name).used + files = os.listdir(os.path.join(str(usb_mount))) + for f in files: + if f.endswith('.tlz'): + folder_size_to_remove += os.path.getsize(os.path.join(config.usb_mount, f)) + elif config.distro == "trinity-rescue": + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "trk3")).used + folder_size_to_remove += disk_usage(usb_mount + "/multibootusb/" + config.uninstall_distro_dir_name).used + else: + + folder_size_to_remove = disk_usage(os.path.join(usb_mount, "multibootusb", config.uninstall_distro_dir_name)).used + + thrd = threading.Thread(target=unin_distro, name="uninstall_progress") + initial_usb_size = disk_usage(usb_mount).used + thrd.start() + config.status_text = "Uninstalling " + config.uninstall_distro_dir_name + pbar = progressbar.ProgressBar(maxval=100).start() # bar = progressbar.ProgressBar(redirect_stdout=True) + while thrd.is_alive(): + current_size = disk_usage(usb_mount).used + diff_size = int(initial_usb_size - current_size) + config.percentage = round(float(diff_size) / folder_size_to_remove * 100) + if config.percentage > 100: + config.percentage = 100 + + pbar.update(config.percentage) + + if not thrd.is_alive(): + config.persistence = 0 + config.status_text = "" diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/update_cfg_file.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/update_cfg_file.py new file mode 100644 index 0000000..652efc6 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/update_cfg_file.py @@ -0,0 +1,383 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: update_cfg_file.py +# Purpose: Module to manipulate distro specific and main config files. +# 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 re +import shutil +from .usb import * +from .gen import * +from .iso import * +from . import config + + +def update_distro_cfg_files(iso_link, usb_disk, distro, persistence=0): + """ + Main function to modify/update distro specific strings on distro config files. + :return: + """ + usb_details = details(usb_disk) + usb_mount = usb_details['mount_point'] + usb_uuid = usb_details['uuid'] + usb_label = usb_details['label'] + patch = None + iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir") + if isolinux_bin_exist(config.iso_link): + isolinux_path = os.path.join(iso_cfg_ext_dir, isolinux_bin_path(iso_link)[1:]) + config.status_text = "Updating config files..." + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + log('Updating distro specific config files...') + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith(".cfg") or f.endswith('.CFG') or f.endswith('.lst'): + cfg_file = os.path.join(dirpath, f) + try: + string = open(cfg_file, errors='ignore').read() + except IOError: + log("Unable to read ", cfg_file) + else: + if not distro == "generic": + replace_text = r'\1/multibootusb/' + iso_basename(iso_link) + '/' + string = re.sub(r'([ \t =,])/', replace_text, string) + if distro == "ubuntu": + string = re.sub(r'boot=casper', + 'boot=casper cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ' + 'ignore_bootid root=UUID=' + usb_uuid + ' live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/casper', string) + string = re.sub(r'ui gfxboot', '#ui gfxboot', string) + if not persistence == 0: + string = re.sub(r'boot=casper', 'boot=casper persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "debian" or distro == "debian-install": + string = re.sub(r'boot=live', 'boot=live ignore_bootid live-media-path=/multibootusb/' + + iso_basename(iso_link) + '/live', string) + if not persistence == 0: + string = re.sub(r'boot=live', 'boot=live persistent persistent-path=/multibootusb/' + + iso_basename(iso_link) + "/", string) + + elif distro == "ubuntu-server": + string = re.sub(r'file', + 'cdrom-detect/try-usb=true floppy.allowed_drive_mask=0 ignore_uuid ignore_bootid root=UUID=' + + usb_uuid + ' file', string) + elif distro == "fedora": + string = re.sub(r'root=\S*', 'root=live:UUID=' + usb_uuid, string) + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', 'liveimg live_dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rd.live.dir=/multibootusb/' + + iso_basename(iso_link) + '/LiveOS', string) + elif re.search(r'Solus', string, re.I): + string = re.sub(r'initrd=', 'rd.live.dir=/multibootusb/' + iso_basename(iso_link) + + '/LiveOS initrd=', string) + + if not persistence == 0: + if re.search(r'liveimg', string, re.I): + string = re.sub(r'liveimg', 'liveimg overlay=UUID=' + usb_uuid, string) + elif re.search(r'rd.live.image', string, re.I): + string = re.sub(r'rd.live.image', 'rd.live.image rw rd.live.overlay=UUID=' + usb_uuid, string) + string = re.sub(r' ro ', '', string) + elif distro == 'kaspersky': + if not os.path.exists(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')): + shutil.copyfile(resource_path(os.path.join('data', 'multibootusb', 'syslinux.cfg')), + os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg')) + config_string = kaspersky_config('kaspersky') + config_string = config_string.replace('$INSTALL_DIR', '/multibootusb/' + iso_basename(iso_link)) + config_string = re.sub(r'root=live:UUID=', 'root=live:UUID=' + usb_uuid, config_string) + with open(os.path.join(usb_mount, 'multibootusb', iso_basename(iso_link), 'kaspersky.cfg'), "a") as f: + f.write(config_string) + elif distro == "parted-magic": + if re.search(r'append', string, re.I): + string = re.sub(r'append', 'append directory=/multibootusb/' + iso_basename(iso_link), string, + flags=re.I) + string = re.sub(r'initrd=', 'directory=/multibootusb/' + iso_basename(iso_link) + '/ initrd=', + string) + elif distro == "ubcd": + string = re.sub(r'iso_filename=\S*', 'directory=/multibootusb/' + iso_basename(iso_link), + string, flags=re.I) + elif distro == 'f4ubcd': + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + if not 'multibootusb' in string: + string = re.sub(r'/F4UBCD', '/multibootusb/' + iso_basename(iso_link) + '/F4UBCD', string) + elif distro == "ipcop": + string = re.sub(r'ipcopboot=cdrom\S*', 'ipcopboot=usb', string) + elif distro == "puppy": + string = re.sub(r'pmedia=cd\S*', + 'pmedia=usbflash psubok=TRUE psubdir=/multibootusb/' + iso_basename(iso_link) + '/', + string) + elif distro == "slax": + string = re.sub(r'initrd=', + r'from=/multibootusb/' + iso_basename(iso_link) + '/slax fromusb initrd=', string) + elif distro == "knoppix": + string = re.sub(r'initrd=', 'knoppix_dir=/multibootusb/' + iso_basename(iso_link) + '/KNOPPIX initrd=', string) + #string = re.sub(r'(append)', + # r'\1 knoppix_dir=/multibootusb/' + iso_basename(iso_link) + '/KNOPPIX', + # string) + elif distro == "gentoo": + string = re.sub(r'append ', + 'append real_root=' + usb_disk + ' slowusb subdir=/multibootusb/' + + iso_basename(iso_link) + '/ ', string, flags=re.I) + elif distro == "systemrescuecd": + rows = [] + subdir = '/multibootusb/' + iso_basename(iso_link) + '/' + for line in string.splitlines(True): + addline = True + if re.match(r'append.*--.*', line, flags=re.I): + line = re.sub(r'(append)(.*)--(.*)', r'\1\2subdir=' + subdir + r' --\3 subdir=' + subdir, + line, flags=re.I) + elif re.match(r'append', line, flags=re.I): + line = re.sub(r'(append)', r'\1 subdir=' + subdir, line, flags=re.I) + elif re.match(r'label rescue(32|64)_1', line, flags=re.I): + rows.append(line) + rows.append('append subdir=%s\n' % (subdir,)) + addline = False + + if addline: + rows.append(line) + + string = ''.join(rows) + elif distro == "arch" or distro == "chakra": + string = re.sub(r'isolabel=\S*', + 'isodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I) + string = re.sub(r'isobasedir=', + 'isobasedir=/multibootusb/' + iso_basename(iso_link) + '/', string, flags=re.I) + string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package + if 'manjaro' in string: + if not os.path.exists(os.path.join(usb_mount, '.miso')): + with open(os.path.join(usb_mount, '.miso'), "w") as f: + f.write('') + elif distro == "kaos": + string = re.sub(r'kdeosisolabel=\S*', + 'kdeosisodevice=/dev/disk/by-uuid/' + usb_uuid, string, flags=re.I) + string = re.sub(r'append', + 'append kdeosisobasedir=/multibootusb/' + iso_basename(iso_link) + '/kdeos/', string, flags=re.I) + string = re.sub(r'ui gfxboot', '# ui gfxboot', string) # Bug in the isolinux package + elif distro == "suse" or distro == "opensuse": + if re.search(r'opensuse_12', string, re.I): + string = re.sub(r'append', + 'append loader=syslinux isofrom_system=/dev/disk/by-uuid/' + usb_uuid + ":/" + + iso_name(iso_link), string, flags=re.I) + else: + string = re.sub(r'append', + 'append loader=syslinux isofrom_device=/dev/disk/by-uuid/' + usb_uuid + + ' isofrom_system=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string, flags=re.I) + elif distro == "pclinuxos": + string = re.sub(r'livecd=', + 'fromusb livecd=' + '/multibootusb/' + iso_basename(iso_link) + '/', + string) + string = re.sub(r'prompt', '#prompt', string) + string = re.sub(r'ui gfxboot.com', '#ui gfxboot.com', string) + string = re.sub(r'timeout', '#timeout', string) + elif distro == "wifislax": + string = re.sub(r'vmlinuz', + 'vmlinuz from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + string = re.sub(r'vmlinuz2', + 'vmlinuz2 from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + elif distro == "porteus": + string = re.sub(r'APPEND', + 'APPEND from=/multibootusb/' + iso_basename(iso_link) + ' noauto', string) + string = re.sub(r'vmlinuz2', + 'vmlinuz2 from=multibootusb/' + iso_basename(iso_link) + ' noauto', string) + elif distro == "hbcd": + if not 'multibootusb' in string: + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', string) + elif distro == "zenwalk": + string = re.sub(r'initrd=', + 'from=/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link) + ' initrd=', + string) + elif distro == "mageialive": + string = re.sub(r'LABEL=\S*', 'LABEL=' + usb_label, string) + elif distro == "antix": + string = re.sub(r'APPEND', 'image_dir=/multibootusb/' + iso_basename(iso_link), string) + elif distro == "solydx": + string = re.sub(r'live-media-path=', 'live-media-path=/multibootusb/' + iso_basename(iso_link), + string) + elif distro == "salix-live": + string = re.sub(r'iso_path', '/multibootusb/' + iso_basename(iso_link) + '/' + iso_name(iso_link), + string) + #string = re.sub(r'initrd', 'from=/multibootusb/' + iso_basename(iso_link) + '/' + ' initrd', string) + elif distro == 'alt-linux': + string = re.sub(r':cdrom', ':disk', string) + elif distro == 'fsecure': + string = re.sub(r'APPEND ramdisk_size', 'APPEND noprompt ' + 'knoppix_dir=/multibootusb/' + iso_basename(iso_link) + + '/KNOPPIX ramdisk_size', string) + + config_file = open(cfg_file, "w") + config_file.write(string) + config_file.close() + + update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro) + + +def update_mbusb_cfg_file(iso_link, usb_uuid, usb_mount, distro): + """ + Update main multibootusb suslinux.cfg file after distro is installed. + :return: + """ + log('Updating multibootusb config file...') + sys_cfg_file = os.path.join(usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(usb_mount, "multibootusb", iso_basename(iso_link)) + if os.path.exists(sys_cfg_file): + + if distro == "hbcd": + if os.path.exists(os.path.join(usb_mount, "multibootusb", "menu.lst")): + _config_file = os.path.join(usb_mount, "multibootusb", "menu.lst") + config_file = open(_config_file,"w") + string = re.sub(r'/HBCD', '/multibootusb/' + iso_basename(iso_link) + '/HBCD', _config_file) + config_file.write(string) + config_file.close() + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") + elif distro == "Windows": + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL chain.c32 hd0 1 ntldr=/bootmgr" + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'f4ubcd': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("KERNEL grub.exe" + "\n") + config_file.write('APPEND --config-file=/multibootusb/' + iso_basename(config.iso_link) + '/menu.lst' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'kaspersky': + if os.path.exists(sys_cfg_file): + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + config_file.write("CONFIG " + '/multibootusb/' + iso_basename(config.iso_link) + '/kaspersky.cfg' + "\n") + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + elif distro == 'grub4dos': + update_menu_lst() + elif distro == 'grub4dos_iso': + update_grub4dos_iso_menu() + else: + # admin.adminCmd(["mount", "-o", "remount,rw", config.usb_disk]) + config_file = open(sys_cfg_file, "a") + config_file.write("#start " + iso_basename(iso_link) + "\n") + config_file.write("LABEL " + iso_basename(iso_link) + "\n") + config_file.write("MENU LABEL " + iso_basename(iso_link) + "\n") + if distro == "salix-live": + if os.path.exists(os.path.join(config.usb_mount, 'multibootusb', iso_basename(iso_link), 'boot', 'grub2-linux.img')): + config_file.write( + "LINUX " + '/multibootusb/' + iso_basename(iso_link) + '/boot/grub2-linux.img' + "\n") + else: + config_file.write("BOOT " + '/multibootusb/' + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/' + distro + '.bs' + "\n") + elif distro == "pclinuxos": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/isolinux/vmlinuz' + "\n") + config_file.write("append livecd=livecd root=/dev/rd/3 acpi=on vga=788 keyb=us vmalloc=256M nokmsboot " + "fromusb root=UUID=" + usb_uuid + " bootfromiso=/multibootusb/" + + iso_basename(iso_link) + "/" + iso_name(iso_link) + " initrd=/multibootusb/" + + iso_basename(iso_link) + '/isolinux/initrd.gz' + "\n") + elif distro == "mentest": + config_file.write("kernel " + '/multibootusb/' + iso_basename(iso_link) + '/BOOT/MEMTEST.IMG\n') + + elif distro == "sgrubd2": + config_file.write("LINUX memdisk\n") + config_file.write("INITRD " + "/multibootusb/" + iso_basename(iso_link) + '/' + iso_name(iso_link) + '\n') + config_file.write("APPEND iso\n") + elif distro == 'ReactOS': + config_file.write("COM32 mboot.c32" + '\n') + config_file.write("APPEND /loader/setupldr.sys" + '\n') + elif distro == 'pc-unlocker': + config_file.write("kernel ../ldntldr" + '\n') + config_file.write("append initrd=../ntldr" + '\n') + else: + if isolinux_bin_exist(config.iso_link) is True: + if distro == "generic": + distro_syslinux_install_dir = isolinux_bin_dir(iso_link) + if not isolinux_bin_dir(iso_link) == "/": + distro_sys_install_bs = os.path.join(usb_mount, isolinux_bin_dir(iso_link)) + '/' + distro + '.bs' + else: + distro_sys_install_bs = '/' + distro + '.bs' + else: + distro_syslinux_install_dir = install_dir + distro_syslinux_install_dir = distro_syslinux_install_dir.replace(usb_mount, '') + distro_sys_install_bs = distro_syslinux_install_dir + '/' + isolinux_bin_dir(iso_link) + '/' + distro + '.bs' + + distro_sys_install_bs = "/" + distro_sys_install_bs.replace("\\", "/") # Windows path issue. + + if config.syslinux_version == '3': + config_file.write("CONFIG /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '/isolinux.cfg\n') + config_file.write("APPEND /multibootusb/" + iso_basename(iso_link) + '/' + isolinux_bin_dir(iso_link).replace("\\", "/") + '\n') + else: + config_file.write("BOOT " + distro_sys_install_bs.replace("//", "/") + "\n") + + config_file.write("#end " + iso_basename(iso_link) + "\n") + config_file.close() + + for dirpath, dirnames, filenames in os.walk(install_dir): + for f in filenames: + if f.endswith("isolinux.cfg") or f.endswith("ISOLINUX.CFG"): + if not os.path.exists(os.path.join(dirpath, "syslinux.cfg")) or not os.path.exists(os.path.join(dirpath, "SYSLINUX.CFG")): + shutil.copy2(os.path.join(dirpath, f), os.path.join(dirpath, "syslinux.cfg")) + + +def kaspersky_config(distro): + if distro == 'kaspersky': + return """ +menu label Kaspersky Rescue Disk + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ looptype=squashfs rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz lang=en udev liveimg splash quiet doscsi nomodeset +label text + menu label Kaspersky Rescue Disk - Text Mode + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=/multiboot/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet nox shell noresume doscsi nomodeset +label hwinfo + menu label Kaspersky Hardware Info + kernel $INSTALL_DIR/boot/rescue + append root=live:UUID= live_dir=$INSTALL_DIR/rescue/LiveOS/ subdir=$INSTALL_DIR/rescue/LiveOS/ rootfstype=auto vga=791 init=/linuxrc loop=$INSTALL_DIR/rescue/LiveOS/squashfs.img initrd=$INSTALL_DIR/boot/rescue.igz SLUG_lang=en udev liveimg quiet softlevel=boot nox hwinfo noresume doscsi nomodeset """ + + +def update_menu_lst(): + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + menu_lst = iso_menu_lst_path(config.iso_link).replace("\\", "/") + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("KERNEL grub.exe" + "\n") + f.write('APPEND --config-file=/' + menu_lst + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") + +def update_grub4dos_iso_menu(): + sys_cfg_file = os.path.join(config.usb_mount, "multibootusb", "syslinux.cfg") + install_dir = os.path.join(config.usb_mount, "multibootusb", iso_basename(config.iso_link)) + menu_lst_file = os.path.join(install_dir, 'menu.lst') + with open(menu_lst_file, "w") as f: + f.write("title Boot " + iso_name(config.iso_link) + "\n") + f.write("find --set-root --ignore-floppies --ignore-cd /multibootusb/" + iso_basename(config.iso_link) + '/' + + iso_name(config.iso_link) + "\n") + f.write("map --heads=0 --sectors-per-track=0 /multibootusb/" + iso_basename(config.iso_link) + + '/' + iso_name(config.iso_link) + ' (hd32)' + "\n") + f.write("map --hook" + "\n") + f.write("chainloader (hd32)") + + with open(sys_cfg_file, "a") as f: + f.write("#start " + iso_basename(config.iso_link) + "\n") + f.write("LABEL " + iso_basename(config.iso_link) + "\n") + f.write("MENU LABEL " + iso_basename(config.iso_link) + "\n") + f.write("KERNEL grub.exe" + "\n") + f.write('APPEND --config-file=/multibootusb/' + iso_basename(config.iso_link) + '/' + iso_name(config.iso_link) + "\n") + f.write("#end " + iso_basename(config.iso_link) + "\n") diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/usb.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/usb.py new file mode 100644 index 0000000..9c27c8b --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/scripts/usb.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Name: usb.py +# Purpose: Module to list USB devices and get details under Linux and Windows +# 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 sys +import platform +import os +import shutil +import collections +import ctypes +import subprocess +from . import gen +if platform.system() == 'Linux': + from . import udisks + u = udisks.get_udisks(ver=None) +if platform.system() == 'Windows': + import psutil + import win32com.client + import win32com.client + import wmi + import pythoncom + + +def is_block(usb_disk): + """ + Function to detect if the USB is block device + :param usb_disk: USB disk path + :return: True is devie is block device else False + """ + import stat + if platform.system() == 'Linux': + if len(usb_disk) != 9: + return False + elif platform.system() == 'Windows': + if len(usb_disk) != 2: + return False + else: + return True + try: + mode = os.stat(usb_disk).st_mode + gen.log(mode) + gen.log(stat.S_ISBLK(mode)) + except: + return False + + return stat.S_ISBLK(mode) + + +def disk_usage(mount_path): + """ + Return disk usage statistics about the given path as a (total, used, free) + namedtuple. Values are expressed in bytes. + """ + # Author: Giampaolo Rodola' + # License: MIT + _ntuple_diskusage = collections.namedtuple('usage', 'total used free') + + if platform.system() == "Linux": + st = os.statvfs(mount_path) + free = st.f_bavail * st.f_frsize + total = st.f_blocks * st.f_frsize + used = (st.f_blocks - st.f_bfree) * st.f_frsize + + return _ntuple_diskusage(total, used, free) + + elif platform.system() == "Windows": + + _, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), \ + ctypes.c_ulonglong() + if sys.version_info >= (3,) or isinstance(mount_path, unicode): + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExW + else: + fun = ctypes.windll.kernel32.GetDiskFreeSpaceExA + ret = fun(mount_path, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free)) + if ret == 0: + raise ctypes.WinError() + used = total.value - free.value + + return _ntuple_diskusage(total.value, used, free.value) + else: + raise NotImplementedError("Platform not supported.") + + +def list(partition=1, fixed=None): + """ + List inserted USB devices. + :return: USB devices as list. + """ + devices = [] + if platform.system() == "Linux": + from . import pyudev + import dbus + + + try: + # pyudev is good enough to detect USB devices on modern Linux machines. + gen.log("Using pyudev for detecting USB drives...") + context = pyudev.Context() + if fixed is None: + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + # if device['ID_BUS'] == "usb" and device['DEVTYPE'] == "partition": + if device.get('ID_BUS') in ("usb", "scsi") and device.get('DEVTYPE') == "partition": + # gen.log(device['DEVNAME']) + devices.append(str(device['DEVNAME'])) + else: + for device in context.list_devices(subsystem='block', DEVTYPE='partition'): + devices.append(str(device['DEVNAME'])) + except: + bus = dbus.SystemBus() + try: + # You should come here only if your system does'nt have udev installed. + # We will use udiskd2 for now. + gen.log("Falling back to Udisks2..") + ud_manager_obj = bus.get_object( + 'org.freedesktop.UDisks2', '/org/freedesktop/UDisks2') + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.DBus.ObjectManager') + for k, v in ud_manager.GetManagedObjects().iteritems(): + drive_info = v.get('org.freedesktop.UDisks2.Block', {}) + if drive_info.get('IdUsage') == "filesystem" and not drive_info.get( + 'HintSystem') and not drive_info.get('ReadOnly'): + device = drive_info.get('Device') + device = bytearray(device).replace( + b'\x00', b'').decode('utf-8') + devices.append(device) + except: + try: + # You must be using really old distro. Otherwise, the code + # should not reach here. + gen.log("Falling back to Udisks1...") + ud_manager_obj = bus.get_object( + "org.freedesktop.UDisks", "/org/freedesktop/UDisks") + ud_manager = dbus.Interface( + ud_manager_obj, 'org.freedesktop.UDisks') + for dev in ud_manager.EnumerateDevices(): + device_obj = bus.get_object( + "org.freedesktop.UDisks", dev) + device_props = dbus.Interface( + device_obj, dbus.PROPERTIES_IFACE) + if device_props.Get('org.freedesktop.UDisks.Device', + "DriveConnectionInterface") == "usb" and device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceIsPartition"): + if device_props.Get('org.freedesktop.UDisks.Device', "DeviceIsMounted"): + device_file = device_props.Get( + 'org.freedesktop.UDisks.Device', "DeviceFile") + devices.append(device_file) + except: + gen.log("No USB device found...") + + elif platform.system() == "Windows": + if fixed is not None: + for drive in psutil.disk_partitions(): + if 'cdrom' in drive.opts or drive.fstype == '': + # Skip cdrom drives or the disk with no filesystem + continue + devices.append(drive[0][:-1]) + else: + try: + # Try new method using psutil. It should also detect USB 3.0 (but not tested by me) + for drive in psutil.disk_partitions(): + if 'cdrom' in drive.opts or drive.fstype == '': + # Skip cdrom drives or the disk with no filesystem + continue + if 'removable' in drive.opts: + devices.append(drive[0][:-1]) + except: + # Revert back to old method if psutil fails (which is unlikely) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + oDrives = oFS.Drives + for drive in oDrives: + if drive.DriveType == 1 and drive.IsReady: + devices.append(drive) + + if devices: + return devices + else: + gen.log("No USB device found...") + return None + + +def details_udev(usb_disk_part): + """ + Get details of USB partition using udev + """ + if platform.system() == "Linux": + from . import pyudev + """ + Try with PyUdev to get the details of USB disks. + This is the easiest and reliable method to find USB details. + Also, it is a standalone package and no dependencies are required. + """ + # gen.log "Using PyUdev for detecting USB details..." + context = pyudev.Context() + for device in context.list_devices(subsystem='block', DEVTYPE='partition', + ID_FS_USAGE="filesystem", ID_TYPE="disk", + ID_BUS="usb"): + fdisk_cmd_out = subprocess.check_output('fdisk -l ' + usb_disk_part, shell=True) + if b'Extended' in fdisk_cmd_out: + mount_point = '' + uuid = 'No_UUID' + file_system = 'No_FS' + vendor = 'No_Vendor' + model = 'No_Model' + label = 'No_Label' + elif device.get('ID_BUS') in ("usb", "scsi") and device.get('DEVTYPE') == "partition": + if (device['DEVNAME']) == usb_disk_part: + uuid = str(device['ID_FS_UUID']) + file_system = str(device['ID_FS_TYPE']) + try: + label = str(device['ID_FS_LABEL']) + except: + label = "No_Label" + mount_point = u.mount(usb_disk_part) + # mount_point = os.popen('findmnt -nr -o target -S %s' % usb_disk_part).read().strip() + # Convert the hex string of space to empty space. + mount_point = mount_point.replace('\\x20', ' ') + try: + vendor = str(device['ID_VENDOR']) + except: + vendor = str('No_Vendor') + try: + model = str(device['ID_MODEL']) + except: + model = str('No_Model') + + if not mount_point == '': + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + mount_point = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details_udisks2(usb_disk_part): + """ + Get details of USB disk detail. + usb_disk_part: It is the partition of an USB removable disk. + """ + import dbus + bus = dbus.SystemBus() + bd = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2/block_devices%s'%usb_disk_part[4:]) + device = bd.Get('org.freedesktop.UDisks2.Block', 'Device', dbus_interface='org.freedesktop.DBus.Properties') + device = bytearray(device).replace(b'\x00', b'').decode('utf-8') + uuid = bd.Get('org.freedesktop.UDisks2.Block', 'IdUUID', dbus_interface='org.freedesktop.DBus.Properties') + file_system = bd.Get('org.freedesktop.UDisks2.Block', 'IdType', dbus_interface='org.freedesktop.DBus.Properties') + mount_point = bd.Get('org.freedesktop.UDisks2.Filesystem', 'MountPoints', dbus_interface='org.freedesktop.DBus.Properties') + if mount_point: + # mount_point = str(bytearray(mount_point[0]).decode('utf-8').replace(b'\x00', b'')) + mount_point = bytearray(mount_point[0]).replace(b'\x00', b'').decode('utf-8') + else: + try: + mount_point = u.mount(usb_disk_part) + except: + mount_point = "No_Mount" + try: + label = bd.Get('org.freedesktop.UDisks2.Block', 'IdLabel', dbus_interface='org.freedesktop.DBus.Properties') + except: + label = "No_Label" + usb_drive_id = (bd.Get('org.freedesktop.UDisks2.Block', 'Drive', dbus_interface='org.freedesktop.DBus.Properties')) + bd1 = bus.get_object('org.freedesktop.UDisks2', usb_drive_id) + try: + vendor = bd1.Get('org.freedesktop.UDisks2.Drive', 'Vendor', dbus_interface='org.freedesktop.DBus.Properties') + except: + vendor = str('No_Vendor') + try: + model = bd1.Get('org.freedesktop.UDisks2.Drive', 'Model', dbus_interface='org.freedesktop.DBus.Properties') + except: + model = str('No_Model') + if not mount_point == "No_Mount": + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + else: + size_total = str('No_Mount') + size_used = str('No_Mount') + size_free = str('No_Mount') + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def bytes2human(n): + """ + Convert the size to human readable format + Authored by 'Giampaolo Rodolà' and original link is:- + http://code.activestate.com/recipes/577972-disk-usage/ + """ + try: + n = int(n) + except: + return 'Unknown' + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = {} + for i, s in enumerate(symbols): + prefix[s] = 1 << (i + 1) * 10 + for s in reversed(symbols): + if n >= prefix[s]: + value = float(n) / prefix[s] + return '%.1f%s' % (value, s) + return "%sB" % n + + +def win_disk_details(disk_drive): + """ + Populate and get details of an USB disk under windows. Minimum required windows version is Vista. + :param disk_drive: USB disk like 'G:' + :return: See the details(usb_disk_part) function for return values. + """ + pythoncom.CoInitialize() + vendor = 'Not_Found' + model = 'Not_Found' + selected_usb_part = str(disk_drive) + oFS = win32com.client.Dispatch("Scripting.FileSystemObject") + d = oFS.GetDrive(oFS.GetDriveName(oFS.GetAbsolutePathName(selected_usb_part))) + selected_usb_device = d.DriveLetter + label = (d.VolumeName).strip() + if not label.strip(): + label = "No_label" + mount_point = selected_usb_device + ":\\" + serno = "%X" % (int(d.SerialNumber) & 0xFFFFFFFF) + uuid = serno[:4] + '-' + serno[4:] + file_system = (d.FileSystem).strip() + size_total = shutil.disk_usage(mount_point)[0] + size_used = shutil.disk_usage(mount_point)[1] + size_free = shutil.disk_usage(mount_point)[2] + ''' + # The below code works only from vista and above. I have removed it as many people reported that the software + # was not working under windows xp. Even then, it is significantly slow if 'All Drives' option is checked. + # Removing the code doesn't affect the functionality as it is only used to find vendor id and model of the drive. + c = wmi.WMI() + for physical_disk in c.Win32_DiskDrive(InterfaceType="USB"): + for partition in physical_disk.associators("Win32_DiskDriveToDiskPartition"): + for logical_disk in partition.associators("Win32_LogicalDiskToPartition"): + if logical_disk.Caption == disk_drive: + vendor = (physical_disk.PNPDeviceID.split('&VEN_'))[1].split('&PROD_')[0] + model = (physical_disk.PNPDeviceID.split('&PROD_'))[1].split('&REV_')[0] + ''' + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + + +def details(usb_disk_part): + """ + Populate and get details of an USB disk. + :param usb_disk_part: USB disk. Example.. "/dev/sdb1" on Linux and "D:\" on Windows. + :return: label == > returns name/label of an inserted USB device. + mount_point == > returns mount path of an inserted USB device. + uuid == > returns uuid of an inserted USB device. + file_system == > returns type of filesystem of an inserted USB device. + device == > returns device path of an inserted USB device. + size_total == > returns total size in MB/GB of an inserted USB device. + size_free == > returns free size in MB/GB of an inserted USB device. + size_used == > returns used size in MB/GB of an inserted USB device. + vendor == > returns the name of the manufacturer. + model == > returns the model name of the USB. + """ + if platform.system() == 'Linux': + try: + udev = details_udev(usb_disk_part) + uuid = udev['uuid'] + file_system = udev['file_system'] + label = udev['label'] + mount_point = udev['mount_point'] + size_total = udev['size_total'] + size_used = udev['size_used'] + size_free = udev['size_free'] + vendor = udev['vendor'] + model = udev['model'] + except: + udisks2 = details_udisks2(usb_disk_part) + uuid = udisks2['uuid'] + file_system = udisks2['file_system'] + label = udisks2['label'] + mount_point = udisks2['mount_point'] + size_total = udisks2['size_total'] + size_used = udisks2['size_used'] + size_free = udisks2['size_free'] + vendor = udisks2['vendor'] + model = udisks2['model'] + elif platform.system() == 'Windows': + win_details = win_disk_details(usb_disk_part) + uuid = win_details['uuid'] + file_system = win_details['file_system'] + label = win_details['label'] + mount_point = win_details['mount_point'] + size_total = win_details['size_total'] + size_used = win_details['size_used'] + size_free = win_details['size_free'] + vendor = win_details['vendor'] + model = win_details['model'] + + return {'uuid': uuid, 'file_system': file_system, 'label': label, 'mount_point': mount_point, + 'size_total': size_total, 'size_used': size_used, 'size_free': size_free, + 'vendor': vendor, 'model': model} + +if __name__ == '__main__': + usb_devices = list() + if usb_devices is not None: + for dev in usb_devices: + gen.log(details(dev)) diff --git a/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/setup.py b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/setup.py new file mode 100644 index 0000000..a399520 --- /dev/null +++ b/deb_dist/tmp_sdist_dsc/multibootusb-8.5.0/setup.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Name: setup.py +# Purpose: Module to create packages or install multibootusb package from source +# 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 + +from distutils.core import setup +#from setuptools import setup, find_packages +import os +import sys +from scripts.gen import mbusb_version + +Version = mbusb_version() +print(Version) +setup( + name='multibootusb', + version=Version, + packages=['scripts', 'scripts.pyudev', 'scripts.pyudev.device', 'scripts.pyudev._ctypeslib', 'scripts.pyudev._os', + 'scripts.gui', 'scripts.progressbar'], + #packages=find_packages(), + scripts=['multibootusb', 'multibootusb-pkexec'], + platforms=['Linux'], + url='http://multibootusb.org/', + license='General Public License (GPL)', + author='Sundar', + author_email='feedback.multibootusb@gmail.com', + description='Create multi boot live Linux on a USB disk...', + long_description='multibootusb is an advanced cross-platform application for installing/uninstalling Linux operating systems on to a single USB flash drives.', + data_files=[("/usr/share/applications", ["data/multibootusb.desktop"]), + ('/usr/share/pixmaps', ["data/tools/multibootusb.png"]), + ('/usr/share/polkit-1/actions/', ['org.debian.pkexec.run-multibootusb.policy']), + ('/usr/share/multibootusb/data/tools', ["data/tools/mbr.bin"]), + ('/usr/share/multibootusb/data', ["data/version.txt"]), + ('/usr/share/multibootusb/data/tools', ["data/tools/multibootusb.png"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/dd.exe"]), + ('/usr/share/multibootusb/data/tools/dd', ["data/tools/dd/diskio.dll"]), + ('/usr/share/multibootusb/data/tools/mkfs', ["data/tools/mkfs/mke2fs.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/chain.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/bg.png"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/extlinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/grub.exe"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/memdisk"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.c32"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/menu.lst"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/syslinux.cfg"]), + ('/usr/share/multibootusb/data/tools/multibootusb', ["data/multibootusb/vesamenu.c32"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_modules.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_linux_64.zip"]), + ('/usr/share/multibootusb/data/tools/syslinux', ["data/tools/syslinux/syslinux_windows.zip"])] +) diff --git a/stdeb.cfg b/stdeb.cfg new file mode 100644 index 0000000..b3fe20d --- /dev/null +++ b/stdeb.cfg @@ -0,0 +1,7 @@ +[DEFAULT] + Package: multibootusb + Depends3: python3-pyqt5, parted, util-linux, mtools, python3-dbus, p7zip-full, python3-six + Build-Depends: python3-all + Section: system + XS-Python-Version: = 3.5 + Debian-Version: 1 \ No newline at end of file