File diff 000000000000 → b168d439d9a0
cdemu/__init__.py
Show inline comments
 
new file 100644
 
from event.sync import EventDispatcher
 
from configparser import ConfigParser
 
import os
 
from .device import CDEmuDevice
 
from .proxy import CDEmuDaemonProxy, OP_DEV_ID, OP_DPM_EMU, OP_TR_EMU, OP_BS_EMU
 
from mount import mount
 

	
 

	
 
class CDEmuException(Exception):
 
    """
 
    Exception class that encompasses exceptions happening while interacting with CDEmu class
 
    """
 
    pass
 

	
 

	
 
class CDEmu(EventDispatcher):
 
    def __init__(self, state_file=None, config=None):
 
        """
 
        High-level abstraction for CDEmu daemon. Includes following features:
 
        1) Automatically restart daemon if it is stopped(fails?)
 
        2) Automatically unmount device before ejecting or removing it
 
        3) Automatically restore daemon state on startup
 
        """
 
        super().__init__(
 
            'daemon_started',
 
            'daemon_stopped',
 
            'device_status_changed',
 
            'device_option_changed',
 
            'devices_changed'
 
        )
 

	
 
        self.devices = []
 

	
 
        self.state_file = state_file
 
        self.state = ConfigParser()
 
        if state_file and os.path.isfile(state_file):
 
            self.state.read(state_file)
 

	
 
        if not self.state.has_section('main'):
 
            self.change_option('main', 'devices', '1')
 
            self.change_option('main', 'autorestart', '1')
 
            self.change_option('dev0', 'dpm-emulation', '0')
 
            self.change_option('dev0', 'tr-emulation', '0')
 
            self.change_option('dev0', 'bad-sector-emulation', '0')
 
            self.save_options()
 

	
 
        self.daemon = CDEmuDaemonProxy()
 
        self.daemon.add_handler('daemon_started', self.on_daemon_started)
 
        self.daemon.add_handler('daemon_stopped', self.on_daemon_stopped)
 
        self.daemon.add_handler('device_mapping_ready', self.on_device_mapping_ready)
 
        self.daemon.add_handler('device_added', self.on_device_added)
 
        self.daemon.add_handler('device_removed', self.on_device_removed)
 
        self.daemon.add_handler('device_status_changed', self.on_device_status_changed)
 
        self.daemon.add_handler('device_option_changed', self.on_device_option_changed)
 
        if self.daemon.is_running:
 
            self.on_daemon_started()
 

	
 
    def get_option(self, section, option):
 
        if not self.state.has_section(section):
 
            return None
 
        if option not in self.state[section]:
 
            return None
 
        value = self.state[section][option]
 
        if value.startswith('"'):
 
            value = value[1:-1]
 
        return value
 

	
 
    def change_option(self, section, option, value, save=False):
 
        value = str(value)
 
        if value.startswith(' ') or value.startswith('"') or value.endswith(' '):
 
            value = '"{}"'.format(value)
 
        if not self.state.has_section(section):
 
            self.state.add_section(section)
 
        self.state.set(section, option, value)
 
        if save and self.state_file:
 
            with open(self.state_file, 'w') as f:
 
                self.state.write(f)
 

	
 
    def save_options(self):
 
        if self.state_file:
 
            with open(self.state_file, 'w') as f:
 
                self.state.write(f)
 

	
 
    def on_daemon_started(self):
 
        self.dispatch('daemon_started')
 
        need_devices = int(self.get_option('main', 'devices'))
 
        have_devices = self.daemon.get_number_of_devices()
 
        while have_devices < need_devices:
 
            self.daemon.add_device()
 
            have_devices += 1
 
        while have_devices > need_devices:
 
            self.daemon.remove_device()
 
            have_devices -= 1
 
        for x in range(need_devices):
 
            if len(self.devices) <= x:
 
                self.devices.append(self._create_device(len(self.devices)))
 

	
 
            if self.state.has_section('dev{}'.format(x)):
 
                sect = 'dev{}'.format(x)
 
            else:
 
                sect = 'main'
 
            if self.get_option(sect, OP_DPM_EMU):
 
                self.daemon.device_set_option(x, OP_DPM_EMU, self.get_option(sect, OP_DPM_EMU) == '1')
 
            if self.get_option(sect, OP_TR_EMU):
 
                self.daemon.device_set_option(x, OP_TR_EMU, self.get_option(sect, OP_TR_EMU) == '1')
 
            if self.get_option(sect, OP_BS_EMU):
 
                self.daemon.device_set_option(x, OP_BS_EMU, self.get_option(sect, OP_BS_EMU) == '1')
 
            if self.get_option(sect, 'vendor'):
 
                vendor = self.get_option(sect, 'vendor')
 
                product = self.get_option(sect, 'product')
 
                revision = self.get_option(sect, 'revision')
 
                vendor_specific = self.get_option(sect, 'vendor_specific')
 
                self.daemon.device_set_option(x, OP_DEV_ID, (vendor, product, revision, vendor_specific))
 
            if self.get_option(sect, 'img'):
 
                self.load(self.devices[x], self.get_option(sect, 'img'))
 
            else:
 
                self.unload(self.devices[x])
 

	
 
            sr_map, sg_map = self.daemon.device_get_mapping(x)
 
            if sr_map or sg_map:
 
                self.on_device_mapping_ready(x)
 

	
 
    def on_daemon_stopped(self):
 
        self.dispatch('daemon_stopped')
 
        if self.get_option('main', 'autorestart') == '1':
 
            self.daemon.connect()
 

	
 
    def on_device_mapping_ready(self, number):
 
        if number >= len(self.devices):
 
            return
 

	
 
        device = self.devices[number]
 
        device._mapped_sr, device._mapped_sg = self.daemon.device_get_mapping(number)
 
        loaded, img = self.daemon.device_get_status(number)
 
        if loaded and len(img) > 0:
 
            device._img = img[0]
 

	
 
        device._dpm = self.daemon.device_get_option(number, 'dpm-emulation')
 
        device._tr = self.daemon.device_get_option(number, 'tr-emulation')
 
        device._bs = self.daemon.device_get_option(number, 'bad-sector-emulation')
 
        device._vendor, device._product, device._revision, device._vendor_specific = self.daemon.device_get_option(
 
            number, 'device-id')
 

	
 
        section = 'dev{}'.format(number)
 
        self.change_option(section, OP_DPM_EMU, device._dpm)
 
        self.change_option(section, OP_TR_EMU, device._tr)
 
        self.change_option(section, OP_BS_EMU, device._bs)
 

	
 
        self.change_option(section, 'vendor', device._vendor)
 
        self.change_option(section, 'product', device._product)
 
        self.change_option(section, 'revision', device._revision)
 
        self.change_option(section, 'vendor_specific', device._vendor_specific)
 
        self.save_options()
 

	
 
        self.dispatch('devices_changed', number)
 

	
 
    def _create_device(self, id):
 
        device = CDEmuDevice(id, self)
 
        return device
 

	
 
    def load(self, device, img):
 
        self.daemon.device_load(device._device_id, [img], {})
 

	
 
    def unload(self, device):
 
        try:
 
            if device.mapped_sr and mount.is_mounted(device.mapped_sr):
 
                mount.unmount(device.mapped_sr)
 
        except mount.MountException as e:
 
            raise CDEmuException(str(e))
 
        self.daemon.device_unload(device._device_id)
 

	
 
    def on_device_added(self):
 
        # Add device and send event
 
        devices = self.daemon.get_number_of_devices()
 
        self.change_option('main', 'devices', devices, True)
 
        while len(self.devices) < devices:
 
            self.devices.append(self._create_device(len(self.devices)))
 

	
 
    def on_device_removed(self):
 
        # Remove device and send event
 
        devices = self.daemon.get_number_of_devices()
 
        while len(self.devices) > devices:
 
            self.devices.pop()
 
        self.dispatch('devices_changed')
 

	
 
    def on_device_status_changed(self, id):
 
        loaded, img = self.daemon.device_get_status(id)
 
        if loaded and len(img) > 0:
 
            self.devices[id]._img = img[0]
 
            self.change_option('dev{}'.format(id), 'img', self.devices[id]._img, True)
 
        else:
 
            self.devices[id]._img = None
 
            self.change_option('dev{}'.format(id), 'img', '', True)
 
        self.devices[id].dispatch('change')
 

	
 
    def on_device_option_changed(self, id, option):
 
        value = self.daemon.device_get_option(id, option)
 
        section = 'dev{}'.format(id)
 
        if option == OP_DPM_EMU:
 
            self.devices[id]._dpm = value
 
            self.change_option(section, OP_DPM_EMU, value)
 
        elif option == OP_TR_EMU:
 
            self.devices[id]._tr = value
 
            self.change_option(section, OP_TR_EMU, value)
 
        elif option == OP_BS_EMU:
 
            self.devices[id]._bs = value
 
            self.change_option(section, OP_BS_EMU, value)
 
        elif option == OP_DEV_ID:
 
            self.devices[id]._vendor, self.devices[id]._product, \
 
            self.devices[id]._revision, self.devices[id]._vendor_specific = value
 
            self.change_option(section, 'vendor', self.devices[id]._vendor)
 
            self.change_option(section, 'product', self.devices[id]._product)
 
            self.change_option(section, 'revision', self.devices[id]._revision)
 
            self.change_option(section, 'vendor_specific', self.devices[id]._vendor_specific)
 
        self.save_options()
 
        self.devices[id].dispatch('change')
 

	
 
    def set_device_option(self, device_number, option_name, option_value):
 
        self.daemon.device_set_option(device_number, option_name, option_value)
 

	
 
    def add_device(self):
 
        self.daemon.add_device()
 

	
 
    def remove_device(self):
 
        self.daemon.remove_device()
 

	
 
    def get_devices(self):
 
        return self.devices.copy()
 

	
 
    def is_daemon_ready(self):
 
        return self.daemon.is_running
 
\ No newline at end of file