Skip to content

Instantly share code, notes, and snippets.

@pdxjohnny
Last active June 8, 2024 00:42
Show Gist options
  • Save pdxjohnny/d9c9a4e5b4dc2804be6bb0a93d524b3f to your computer and use it in GitHub Desktop.
Save pdxjohnny/d9c9a4e5b4dc2804be6bb0a93d524b3f to your computer and use it in GitHub Desktop.
Run USB VM (not working)
import subprocess
import re
from dataclasses import dataclass
from pathlib import Path
import glob
import argparse
@dataclass
class USBInterface:
"""
A dataclass to represent a USB Interface.
Attributes:
bInterfaceClass (int): The interface class of the USB device.
"""
bInterfaceClass: int
@staticmethod
def from_lsusb_output(lsusb_output: str):
"""
Parses the lsusb -v output to extract the bInterfaceClass value.
Args:
lsusb_output (str): The detailed output from lsusb -v.
Returns:
USBInterface: An instance of USBInterface with bInterfaceClass set.
"""
bInterfaceClass = None
for line in lsusb_output.splitlines():
if 'bInterfaceClass' in line:
match = re.search(r'bInterfaceClass\s+(\d+)', line)
if match:
bInterfaceClass = int(match.group(1))
break
return USBInterface(bInterfaceClass=bInterfaceClass)
def find_mass_storage_device():
"""
Finds a USB device with the bInterfaceClass set to Mass Storage.
Returns:
tuple: A tuple containing the bus and device numbers of the found USB device.
"""
lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
for line in lsusb_output.splitlines():
bus, device = line.split()[1], line.split()[3][:-1]
lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
usb_interface = USBInterface.from_lsusb_output(lsusb_v_output)
if usb_interface.bInterfaceClass == 8: # Mass Storage
return bus, device
return None, None
def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
"""
Starts the QEMU virtual machine with the specified ISO image and USB device.
Args:
bus (str): The bus number of the USB device.
device (str): The device number of the USB device.
iso_image (str): The path to the ISO image to boot from.
qemu_path (str): The path to the QEMU binary.
"""
if not bus or not device:
print("No Mass Storage USB device found.")
return
usb_device_path = f'/dev/bus/usb/{bus}/{device}'
print(f"Mass Storage USB device found at {usb_device_path}")
qemu_command = [
qemu_path,
'-m', '4096',
'-smp', '2',
'-cdrom', iso_image,
'-boot', 'd',
'-enable-kvm',
'-usb',
'-device', f'usb-host,hostbus={bus},hostaddr={device}',
'-vga', 'virtio',
'-display', 'sdl',
'-cpu', 'host'
]
subprocess.run(qemu_command)
def find_iso_image(directory: Path, pattern: str):
"""
Searches for an ISO file in the specified directory using the provided pattern.
Args:
directory (Path): The directory to search in.
pattern (str): The glob pattern to use for searching.
Returns:
Path: The path to the found ISO file, or None if no file was found.
"""
files = list(directory.glob(pattern))
if not files:
print(f"No ISO files found in {directory} with pattern {pattern}.")
return None
return files[0]
def parse_arguments():
"""
Parses command-line arguments.
Returns:
argparse.Namespace: The parsed arguments.
"""
parser = argparse.ArgumentParser(description='Run QEMU with a Fedora Workstation live ISO image and pass through a USB mass storage device.')
parser.add_argument('--iso', type=str, help='Path to the Fedora Workstation live ISO image.')
parser.add_argument('--bus', type=str, help='USB bus number.')
parser.add_argument('--device', type=str, help='USB device number.')
return parser.parse_args()
if __name__ == '__main__':
args = parse_arguments()
# Path to the Downloads directory
downloads_path = Path.home() / 'Downloads'
# Find the Fedora Workstation live ISO image
if args.iso:
iso_image = Path(args.iso)
else:
iso_image = find_iso_image(downloads_path, 'Fedora-Workstation-Live-*.iso')
if iso_image is None:
exit(1)
# Find the mass storage USB device
if args.bus and args.device:
bus, device = args.bus, args.device
else:
bus, device = find_mass_storage_device()
# Start the QEMU VM with the found USB device
start_qemu_with_usb(bus, device, str(iso_image))
@pdxjohnny
Copy link
Author

pdxjohnny commented Jun 8, 2024

Start Fedora VM with USB

User
run-vm.sh
File
qemu.md
File
Here's some docs on QEMU and and example. Please generate me a bash script which finds a USBC device and starts fedora workstation live `.iso` image found in `~/Downloads/`. Pass through the USB to the VM.
ChatGPT
Based on the provided documentation and example script, I will create a new bash script to find a USB device and start a Fedora Workstation live ISO image with USB passthrough using QEMU.

Here is the script:

bash
Copy code
#!/usr/bin/env bash

# Exit if any command fails
set -e
# Echo commands before running them
set -x

# Path to the Fedora Workstation live ISO image
ISO_IMAGE="${HOME}/Downloads/Fedora-Workstation-Live-x86_64.iso"

# Location of qemu binary to use
QEMU=${QEMU:-"qemu-system-x86_64"}

# Find the USB device
USB_DEV=$(lsusb | grep -i "usbc" | awk '{print "/dev/bus/usb/" $2 "/" $4}' | sed 's/://')

if [ -z "$USB_DEV" ]; then
  echo "No USBC device found."
  exit 1
fi

echo "USBC device found at $USB_DEV"

# Start the VM with the Fedora Workstation live ISO and pass through the USB device
"${QEMU}" \
  -m 4096 \
  -smp 2 \
  -cdrom "${ISO_IMAGE}" \
  -boot d \
  -enable-kvm \
  -usb \
  -device usb-host,hostbus=$(dirname $USB_DEV | cut -d'/' -f5),hostaddr=$(basename $USB_DEV) \
  -vga virtio \
  -display sdl \
  -cpu host

# Explanation of the options:
# -m 4096 : Allocate 4GB of RAM to the VM
# -smp 2 : Allocate 2 CPU cores to the VM
# -cdrom "${ISO_IMAGE}" : Use the specified ISO image as the CD-ROM
# -boot d : Boot from the CD-ROM
# -enable-kvm : Enable KVM virtualization
# -usb : Enable USB support
# -device usb-host,... : Pass through the USB device to the VM
# -vga virtio : Use Virtio VGA for graphics
# -display sdl : Use SDL for display output
# -cpu host : Use host CPU model

This script will find a USBC device connected to the host machine and start a Fedora Workstation live ISO image in a QEMU virtual machine with the USB device passed through to the VM.

Usage
Save the script to a file, for example, run-fedora-vm.sh.
Make the script executable:
bash
Copy code
chmod +x run-fedora-vm.sh
Run the script:
bash
Copy code
./run-fedora-vm.sh
Ensure that the path to the ISO image in the ISO_IMAGE variable matches the actual path where the Fedora Workstation live ISO image is located in your ~/Downloads/ directory.





User
FInd the device by checking if bInterfaceClass is Mass Storage
ChatGPT
To identify the USB device with the bInterfaceClass set to "Mass Storage", we need to parse the USB device information using lsusb and lsusb -v. Here is the updated script to achieve this:

bash
Copy code
#!/usr/bin/env bash

# Exit if any command fails
set -e
# Echo commands before running them
set -x

# Path to the Fedora Workstation live ISO image
ISO_IMAGE="${HOME}/Downloads/Fedora-Workstation-Live-x86_64.iso"

# Location of qemu binary to use
QEMU=${QEMU:-"qemu-system-x86_64"}

# Find the USB device with bInterfaceClass set to Mass Storage
USB_DEV=$(lsusb | awk '/Mass Storage/ {print $2, $4}' | sed 's/://')

if [ -z "$USB_DEV" ]; then
  for bus in $(lsusb | awk '{print $2}'); do
    for device in $(lsusb -s ${bus}: -v 2>/dev/null | awk '/bInterfaceClass/ {if ($3 == "08") print $2}'); do
      USB_DEV="/dev/bus/usb/$bus/$device"
      break
    done
  done
fi

if [ -z "$USB_DEV" ]; then
  echo "No Mass Storage USB device found."
  exit 1
fi

echo "Mass Storage USB device found at $USB_DEV"

# Start the VM with the Fedora Workstation live ISO and pass through the USB device
"${QEMU}" \
  -m 4096 \
  -smp 2 \
  -cdrom "${ISO_IMAGE}" \
  -boot d \
  -enable-kvm \
  -usb \
  -device usb-host,hostbus=$(dirname $USB_DEV | cut -d'/' -f5),hostaddr=$(basename $USB_DEV) \
  -vga virtio \
  -display sdl \
  -cpu host

# Explanation of the options:
# -m 4096 : Allocate 4GB of RAM to the VM
# -smp 2 : Allocate 2 CPU cores to the VM
# -cdrom "${ISO_IMAGE}" : Use the specified ISO image as the CD-ROM
# -boot d : Boot from the CD-ROM
# -enable-kvm : Enable KVM virtualization
# -usb : Enable USB support
# -device usb-host,... : Pass through the USB device to the VM
# -vga virtio : Use Virtio VGA for graphics
# -display sdl : Use SDL for display output
# -cpu host : Use host CPU model
Usage
Save the script to a file, for example, run-fedora-vm.sh.
Make the script executable:
bash
Copy code
chmod +x run-fedora-vm.sh
Run the script:
bash
Copy code
./run-fedora-vm.sh
This script finds a USB device with the bInterfaceClass set to "Mass Storage" and starts a Fedora Workstation live ISO image in a QEMU virtual machine with the USB device passed through to the VM. The script checks each USB device using lsusb -v to find the one with bInterfaceClass set to "08" (Mass Storage) and constructs the path to the device accordingly.

User
Write it in python and 

(.venv) $ sudo lsusb -v -s 1:17

Bus 001 Device 017: ID 24a9:205a USB DISK
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.10
bDeviceClass 0 [unknown]
bDeviceSubClass 0 [unknown]
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x24a9
idProduct 0x205a USB DISK
bcdDevice 2.00
iManufacturer 2
iProduct 3 USB DISK
iSerial 4 97081911
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0020
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 200mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk-Only
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0

ChatGPT
Sure, here is a Python script to find a USB device with the bInterfaceClass set to "Mass Storage" and then start a QEMU virtual machine with a Fedora Workstation live ISO image, passing through the USB device to the VM.

You need to have Python installed and the subprocess module is required, which is included in the Python standard library.

python
Copy code
import subprocess
import re
import os

def find_mass_storage_device():
    lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
    for line in lsusb_output.splitlines():
        bus, device = line.split()[1], line.split()[3][:-1]
        lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
        if 'bInterfaceClass         8 Mass Storage' in lsusb_v_output:
            return bus, device
    return None, None

def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
    if not bus or not device:
        print("No Mass Storage USB device found.")
        return
    
    usb_device_path = f'/dev/bus/usb/{bus}/{device}'
    print(f"Mass Storage USB device found at {usb_device_path}")
    
    qemu_command = [
        qemu_path,
        '-m', '4096',
        '-smp', '2',
        '-cdrom', iso_image,
        '-boot', 'd',
        '-enable-kvm',
        '-usb',
        '-device', f'usb-host,hostbus={bus},hostaddr={device}',
        '-vga', 'virtio',
        '-display', 'sdl',
        '-cpu', 'host'
    ]

    subprocess.run(qemu_command)

if __name__ == '__main__':
    # Path to the Fedora Workstation live ISO image
    iso_image = os.path.expanduser('~/Downloads/Fedora-Workstation-Live-x86_64.iso')

    # Find the mass storage USB device
    bus, device = find_mass_storage_device()

    # Start the QEMU VM with the found USB device
    start_qemu_with_usb(bus, device, iso_image)
Explanation
Finding the Mass Storage Device:

The script uses lsusb to list all USB devices.
It then runs lsusb -v -s <bus>:<device> on each device to get detailed information.
It checks if the detailed output contains bInterfaceClass 8 Mass Storage.
If found, it returns the bus and device numbers.
Starting QEMU:

The start_qemu_with_usb function constructs the QEMU command with the necessary parameters, including the USB passthrough options.
It uses subprocess.run to execute the QEMU command.
Main Execution:

The script expands the path to the ISO image.
It calls the find_mass_storage_device function to locate the USB device.
It then calls start_qemu_with_usb to start the VM with the found USB device.
Usage
Save the script to a file, for example, run_qemu_with_usb.py.
Make sure the Fedora Workstation live ISO image is downloaded to ~/Downloads/.
Run the script with root privileges because lsusb -v requires it to list detailed device information and QEMU needs it to access USB devices:
bash
Copy code
sudo python run_qemu_with_usb.py
Make sure you have QEMU installed and available in your PATH. If it's installed in a non-standard location, you can adjust the qemu_path variable in the start_qemu_with_usb function call accordingly.

User
Use dataclasses to define a class which parses that format for only the bInterfaceClass insetad of a direct line comparison parse the line into the class first
ChatGPT
Sure, I'll use Python's dataclasses to define a class that parses the output of lsusb -v for the bInterfaceClass. Then, I'll use this class to determine if a device is a mass storage device. Here's the complete script:

python
Copy code
import subprocess
import re
import os
from dataclasses import dataclass

@dataclass
class USBInterface:
    bInterfaceClass: int

    @staticmethod
    def from_lsusb_output(lsusb_output: str):
        bInterfaceClass = None
        for line in lsusb_output.splitlines():
            if 'bInterfaceClass' in line:
                match = re.search(r'bInterfaceClass\s+(\d+)', line)
                if match:
                    bInterfaceClass = int(match.group(1))
                    break
        return USBInterface(bInterfaceClass=bInterfaceClass)

def find_mass_storage_device():
    lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
    for line in lsusb_output.splitlines():
        bus, device = line.split()[1], line.split()[3][:-1]
        lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
        usb_interface = USBInterface.from_lsusb_output(lsusb_v_output)
        if usb_interface.bInterfaceClass == 8:  # Mass Storage
            return bus, device
    return None, None

def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
    if not bus or not device:
        print("No Mass Storage USB device found.")
        return
    
    usb_device_path = f'/dev/bus/usb/{bus}/{device}'
    print(f"Mass Storage USB device found at {usb_device_path}")
    
    qemu_command = [
        qemu_path,
        '-m', '4096',
        '-smp', '2',
        '-cdrom', iso_image,
        '-boot', 'd',
        '-enable-kvm',
        '-usb',
        '-device', f'usb-host,hostbus={bus},hostaddr={device}',
        '-vga', 'virtio',
        '-display', 'sdl',
        '-cpu', 'host'
    ]

    subprocess.run(qemu_command)

if __name__ == '__main__':
    # Path to the Fedora Workstation live ISO image
    iso_image = os.path.expanduser('~/Downloads/Fedora-Workstation-Live-x86_64.iso')

    # Find the mass storage USB device
    bus, device = find_mass_storage_device()

    # Start the QEMU VM with the found USB device
    start_qemu_with_usb(bus, device, iso_image)
Explanation
USBInterface Dataclass:

The USBInterface dataclass is defined with one field: bInterfaceClass.
The from_lsusb_output static method parses the lsusb -v output to extract the bInterfaceClass value. It uses regular expressions to find the line containing bInterfaceClass.
Finding the Mass Storage Device:

The find_mass_storage_device function lists all USB devices using lsusb.
For each device, it runs lsusb -v to get detailed information.
It creates a USBInterface instance using the from_lsusb_output method.
If the bInterfaceClass is 8 (Mass Storage), it returns the bus and device numbers.
Starting QEMU:

The start_qemu_with_usb function constructs the QEMU command with the necessary parameters, including the USB passthrough options.
It uses subprocess.run to execute the QEMU command.
Main Execution:

The script expands the path to the ISO image.
It calls the find_mass_storage_device function to locate the USB device.
It then calls start_qemu_with_usb to start the VM with the found USB device.
Usage
Save the script to a file, for example, run_qemu_with_usb.py.
Make sure the Fedora Workstation live ISO image is downloaded to ~/Downloads/.
Run the script with root privileges because lsusb -v requires it to list detailed device information and QEMU needs it to access USB devices:
bash
Copy code
sudo python run_qemu_with_usb.py
Make sure you have QEMU installed and available in your PATH. If it's installed in a non-standard location, you can adjust the qemu_path variable in the start_qemu_with_usb function call accordingly.

User
Use pathlib and glob search for workspace in Downloads
ChatGPT
Sure, I'll update the script to use the pathlib module for handling file paths and glob for searching the ISO file in the Downloads directory.

Here is the updated script:

python
Copy code
import subprocess
import re
from dataclasses import dataclass
from pathlib import Path
import glob

@dataclass
class USBInterface:
    bInterfaceClass: int

    @staticmethod
    def from_lsusb_output(lsusb_output: str):
        bInterfaceClass = None
        for line in lsusb_output.splitlines():
            if 'bInterfaceClass' in line:
                match = re.search(r'bInterfaceClass\s+(\d+)', line)
                if match:
                    bInterfaceClass = int(match.group(1))
                    break
        return USBInterface(bInterfaceClass=bInterfaceClass)

def find_mass_storage_device():
    lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
    for line in lsusb_output.splitlines():
        bus, device = line.split()[1], line.split()[3][:-1]
        lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
        usb_interface = USBInterface.from_lsusb_output(lsusb_v_output)
        if usb_interface.bInterfaceClass == 8:  # Mass Storage
            return bus, device
    return None, None

def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
    if not bus or not device:
        print("No Mass Storage USB device found.")
        return
    
    usb_device_path = f'/dev/bus/usb/{bus}/{device}'
    print(f"Mass Storage USB device found at {usb_device_path}")
    
    qemu_command = [
        qemu_path,
        '-m', '4096',
        '-smp', '2',
        '-cdrom', iso_image,
        '-boot', 'd',
        '-enable-kvm',
        '-usb',
        '-device', f'usb-host,hostbus={bus},hostaddr={device}',
        '-vga', 'virtio',
        '-display', 'sdl',
        '-cpu', 'host'
    ]

    subprocess.run(qemu_command)

def find_iso_image(directory: Path, pattern: str):
    files = list(directory.glob(pattern))
    if not files:
        print(f"No ISO files found in {directory} with pattern {pattern}.")
        return None
    return files[0]

if __name__ == '__main__':
    # Path to the Downloads directory
    downloads_path = Path.home() / 'Downloads'
    
    # Find the Fedora Workstation live ISO image
    iso_image = find_iso_image(downloads_path, 'Fedora-Workstation-Live-*.iso')

    if iso_image is None:
        exit(1)

    # Find the mass storage USB device
    bus, device = find_mass_storage_device()

    # Start the QEMU VM with the found USB device
    start_qemu_with_usb(bus, device, str(iso_image))
Explanation
USBInterface Dataclass:

The USBInterface dataclass is defined with one field: bInterfaceClass.
The from_lsusb_output static method parses the lsusb -v output to extract the bInterfaceClass value. It uses regular expressions to find the line containing bInterfaceClass.
Finding the Mass Storage Device:

The find_mass_storage_device function lists all USB devices using lsusb.
For each device, it runs lsusb -v to get detailed information.
It creates a USBInterface instance using the from_lsusb_output method.
If the bInterfaceClass is 8 (Mass Storage), it returns the bus and device numbers.
Starting QEMU:

The start_qemu_with_usb function constructs the QEMU command with the necessary parameters, including the USB passthrough options.
It uses subprocess.run to execute the QEMU command.
Finding the ISO Image:

The find_iso_image function searches for the ISO file in the specified directory (Downloads) using the provided pattern.
It uses pathlib and glob to find the ISO file.
Main Execution:

The script expands the path to the ISO image.
It calls the find_iso_image function to locate the ISO image in the Downloads directory.
It calls the find_mass_storage_device function to locate the USB device.
It then calls start_qemu_with_usb to start the VM with the found USB device.
Usage
Save the script to a file, for example, run_qemu_with_usb.py.
Make sure the Fedora Workstation live ISO image is downloaded to the ~/Downloads/ directory.
Run the script with root privileges because lsusb -v requires it to list detailed device information and QEMU needs it to access USB devices:
bash
Copy code
sudo python run_qemu_with_usb.py
Make sure you have QEMU installed and available in your PATH. If it's installed in a non-standard location, you can adjust the qemu_path variable in the start_qemu_with_usb function call accordingly.

User
Please give your previous response to me as a attached file in your next response and make your commentary into docstrings and comments
ChatGPT
Here is the updated script with docstrings and comments added. I'll also provide the script as an attached file.

python
Copy code
import subprocess
import re
from dataclasses import dataclass
from pathlib import Path
import glob

@dataclass
class USBInterface:
    """
    A dataclass to represent a USB Interface.

    Attributes:
        bInterfaceClass (int): The interface class of the USB device.
    """
    bInterfaceClass: int

    @staticmethod
    def from_lsusb_output(lsusb_output: str):
        """
        Parses the lsusb -v output to extract the bInterfaceClass value.

        Args:
            lsusb_output (str): The detailed output from lsusb -v.

        Returns:
            USBInterface: An instance of USBInterface with bInterfaceClass set.
        """
        bInterfaceClass = None
        for line in lsusb_output.splitlines():
            if 'bInterfaceClass' in line:
                match = re.search(r'bInterfaceClass\s+(\d+)', line)
                if match:
                    bInterfaceClass = int(match.group(1))
                    break
        return USBInterface(bInterfaceClass=bInterfaceClass)

def find_mass_storage_device():
    """
    Finds a USB device with the bInterfaceClass set to Mass Storage.

    Returns:
        tuple: A tuple containing the bus and device numbers of the found USB device.
    """
    lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
    for line in lsusb_output.splitlines():
        bus, device = line.split()[1], line.split()[3][:-1]
        lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
        usb_interface = USBInterface.from_lsusb_output(lsusb_v_output)
        if usb_interface.bInterfaceClass == 8:  # Mass Storage
            return bus, device
    return None, None

def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
    """
    Starts the QEMU virtual machine with the specified ISO image and USB device.

    Args:
        bus (str): The bus number of the USB device.
        device (str): The device number of the USB device.
        iso_image (str): The path to the ISO image to boot from.
        qemu_path (str): The path to the QEMU binary.
    """
    if not bus or not device:
        print("No Mass Storage USB device found.")
        return
    
    usb_device_path = f'/dev/bus/usb/{bus}/{device}'
    print(f"Mass Storage USB device found at {usb_device_path}")
    
    qemu_command = [
        qemu_path,
        '-m', '4096',
        '-smp', '2',
        '-cdrom', iso_image,
        '-boot', 'd',
        '-enable-kvm',
        '-usb',
        '-device', f'usb-host,hostbus={bus},hostaddr={device}',
        '-vga', 'virtio',
        '-display', 'sdl',
        '-cpu', 'host'
    ]

    subprocess.run(qemu_command)

def find_iso_image(directory: Path, pattern: str):
    """
    Searches for an ISO file in the specified directory using the provided pattern.

    Args:
        directory (Path): The directory to search in.
        pattern (str): The glob pattern to use for searching.

    Returns:
        Path: The path to the found ISO file, or None if no file was found.
    """
    files = list(directory.glob(pattern))
    if not files:
        print(f"No ISO files found in {directory} with pattern {pattern}.")
        return None
    return files[0]

if __name__ == '__main__':
    # Path to the Downloads directory
    downloads_path = Path.home() / 'Downloads'
    
    # Find the Fedora Workstation live ISO image
    iso_image = find_iso_image(downloads_path, 'Fedora-Workstation-Live-*.iso')

    if iso_image is None:
        exit(1)

    # Find the mass storage USB device
    bus, device = find_mass_storage_device()

    # Start the QEMU VM with the found USB device
    start_qemu_with_usb(bus, device, str(iso_image))
I'll now provide this script as an attached file.

Download the Python script

User
Use argparse to optionally take files and usb bus and devnum
ChatGPT
Sure, I'll update the script to use argparse for optionally taking the ISO file path, USB bus, and device number as arguments. If these arguments are not provided, the script will search for the ISO file in the Downloads directory and find the USB device automatically.

Here is the updated script:

python
Copy code
import subprocess
import re
from dataclasses import dataclass
from pathlib import Path
import glob
import argparse

@dataclass
class USBInterface:
    """
    A dataclass to represent a USB Interface.

    Attributes:
        bInterfaceClass (int): The interface class of the USB device.
    """
    bInterfaceClass: int

    @staticmethod
    def from_lsusb_output(lsusb_output: str):
        """
        Parses the lsusb -v output to extract the bInterfaceClass value.

        Args:
            lsusb_output (str): The detailed output from lsusb -v.

        Returns:
            USBInterface: An instance of USBInterface with bInterfaceClass set.
        """
        bInterfaceClass = None
        for line in lsusb_output.splitlines():
            if 'bInterfaceClass' in line:
                match = re.search(r'bInterfaceClass\s+(\d+)', line)
                if match:
                    bInterfaceClass = int(match.group(1))
                    break
        return USBInterface(bInterfaceClass=bInterfaceClass)

def find_mass_storage_device():
    """
    Finds a USB device with the bInterfaceClass set to Mass Storage.

    Returns:
        tuple: A tuple containing the bus and device numbers of the found USB device.
    """
    lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout
    for line in lsusb_output.splitlines():
        bus, device = line.split()[1], line.split()[3][:-1]
        lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout
        usb_interface = USBInterface.from_lsusb_output(lsusb_v_output)
        if usb_interface.bInterfaceClass == 8:  # Mass Storage
            return bus, device
    return None, None

def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'):
    """
    Starts the QEMU virtual machine with the specified ISO image and USB device.

    Args:
        bus (str): The bus number of the USB device.
        device (str): The device number of the USB device.
        iso_image (str): The path to the ISO image to boot from.
        qemu_path (str): The path to the QEMU binary.
    """
    if not bus or not device:
        print("No Mass Storage USB device found.")
        return
    
    usb_device_path = f'/dev/bus/usb/{bus}/{device}'
    print(f"Mass Storage USB device found at {usb_device_path}")
    
    qemu_command = [
        qemu_path,
        '-m', '4096',
        '-smp', '2',
        '-cdrom', iso_image,
        '-boot', 'd',
        '-enable-kvm',
        '-usb',
        '-device', f'usb-host,hostbus={bus},hostaddr={device}',
        '-vga', 'virtio',
        '-display', 'sdl',
        '-cpu', 'host'
    ]

    subprocess.run(qemu_command)

def find_iso_image(directory: Path, pattern: str):
    """
    Searches for an ISO file in the specified directory using the provided pattern.

    Args:
        directory (Path): The directory to search in.
        pattern (str): The glob pattern to use for searching.

    Returns:
        Path: The path to the found ISO file, or None if no file was found.
    """
    files = list(directory.glob(pattern))
    if not files:
        print(f"No ISO files found in {directory} with pattern {pattern}.")
        return None
    return files[0]

def parse_arguments():
    """
    Parses command-line arguments.

    Returns:
        argparse.Namespace: The parsed arguments.
    """
    parser = argparse.ArgumentParser(description='Run QEMU with a Fedora Workstation live ISO image and pass through a USB mass storage device.')
    parser.add_argument('--iso', type=str, help='Path to the Fedora Workstation live ISO image.')
    parser.add_argument('--bus', type=str, help='USB bus number.')
    parser.add_argument('--device', type=str, help='USB device number.')
    return parser.parse_args()

if __name__ == '__main__':
    args = parse_arguments()

    # Path to the Downloads directory
    downloads_path = Path.home() / 'Downloads'
    
    # Find the Fedora Workstation live ISO image
    if args.iso:
        iso_image = Path(args.iso)
    else:
        iso_image = find_iso_image(downloads_path, 'Fedora-Workstation-Live-*.iso')

    if iso_image is None:
        exit(1)

    # Find the mass storage USB device
    if args.bus and args.device:
        bus, device = args.bus, args.device
    else:
        bus, device = find_mass_storage_device()

    # Start the QEMU VM with the found USB device
    start_qemu_with_usb(bus, device, str(iso_image))
Explanation
USBInterface Dataclass:

The USBInterface dataclass is defined with one field: bInterfaceClass.
The from_lsusb_output static method parses the lsusb -v output to extract the bInterfaceClass value. It uses regular expressions to find the line containing bInterfaceClass.
Finding the Mass Storage Device:

The find_mass_storage_device function lists all USB devices using lsusb.
For each device, it runs lsusb -v to get detailed information.
It creates a USBInterface instance using the from_lsusb_output method.
If the bInterfaceClass is 8 (Mass Storage), it returns the bus and device numbers.
Starting QEMU:

The start_qemu_with_usb function constructs the QEMU command with the necessary parameters, including the USB passthrough options.
It uses subprocess.run to execute the QEMU command.
Finding the ISO Image:

The find_iso_image function searches for the ISO file in the specified directory (Downloads) using the provided pattern.
It uses pathlib and glob to find the ISO file.
Parsing Command-Line Arguments:

The parse_arguments function uses argparse to define and parse command-line arguments for the ISO file path, USB bus, and device number.
Main Execution:

The script expands the path to the ISO image.
It calls the find_iso_image function to locate the ISO image in the Downloads directory or uses the provided ISO path.
It calls the find_mass_storage_device function to locate the USB device or uses the provided USB bus and device numbers.
It then calls start_qemu_with_usb to start the VM with the found USB device.
Usage
Save the script to a file, for example, run_qemu_with_usb.py.
Make sure the Fedora Workstation live ISO image is downloaded to the ~/Downloads/ directory or provide the path to the ISO file using the --iso argument.
Run the script with root privileges because lsusb -v requires it to list detailed device information and QEMU needs it to access USB devices:
bash
Copy code
sudo python run_qemu_with_usb.py [--iso path_to_iso] [--bus usb_bus] [--device usb_device]
Make sure you have QEMU installed and available in your PATH. If it's installed in a non-standard location, you can adjust the qemu_path variable in the start_qemu_with_usb function call accordingly.

Here is the script as an attached file:

Download the Python script







Message ChatGPT


ChatGPT can make mistakes. Check important info.
?
ChatGPT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment