diff options
-rw-r--r-- | kvmd/plugins/msd/__init__.py | 19 | ||||
-rw-r--r-- | kvmd/plugins/msd/relay/__init__.py | 293 | ||||
-rw-r--r-- | kvmd/plugins/msd/relay/drive.py | 142 | ||||
-rw-r--r-- | kvmd/plugins/msd/relay/gpio.py | 79 | ||||
-rwxr-xr-x | setup.py | 1 | ||||
-rw-r--r-- | testenv/linters/vulture-wl.py | 2 | ||||
-rw-r--r-- | web/kvm/index.html | 24 | ||||
-rw-r--r-- | web/kvm/navbar-msd.pug | 21 | ||||
-rw-r--r-- | web/share/js/kvm/msd.js | 29 |
9 files changed, 21 insertions, 589 deletions
diff --git a/kvmd/plugins/msd/__init__.py b/kvmd/plugins/msd/__init__.py index 21afd86b..ffe4cf76 100644 --- a/kvmd/plugins/msd/__init__.py +++ b/kvmd/plugins/msd/__init__.py @@ -85,21 +85,6 @@ class MsdImageExistsError(MsdOperationError): super().__init__("This image is already exists") -class MsdMultiNotSupported(MsdOperationError): - def __init__(self) -> None: - super().__init__("This MSD does not support storing multiple images") - - -class MsdCdromNotSupported(MsdOperationError): - def __init__(self) -> None: - super().__init__("This MSD does not support CD-ROM switching") - - -class MsdRwNotSupported(MsdOperationError): - def __init__(self) -> None: - super().__init__("This MSD does not support RW switching") - - # ===== class BaseMsdReader: def get_state(self) -> dict: @@ -281,10 +266,6 @@ class MsdFileWriter(BaseMsdWriter): # pylint: disable=too-many-instance-attribu def is_complete(self) -> bool: return (self.__written >= self.__file_size) - def get_file(self) -> aiofiles.base.AiofilesContextManager: - assert self.__file is not None - return self.__file - async def open(self) -> "MsdFileWriter": assert self.__file is None get_logger(1).info("Writing %r image (%d bytes) to MSD ...", self.__name, self.__file_size) diff --git a/kvmd/plugins/msd/relay/__init__.py b/kvmd/plugins/msd/relay/__init__.py deleted file mode 100644 index a93d5e34..00000000 --- a/kvmd/plugins/msd/relay/__init__.py +++ /dev/null @@ -1,293 +0,0 @@ -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program 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 General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see <https://www.gnu.org/licenses/>. # -# # -# ========================================================================== # - - -import asyncio -import contextlib -import dataclasses -import functools - -from typing import AsyncGenerator - -from ....logging import get_logger - -from .... import aiotools - -from ....yamlconf import Option - -from ....validators.basic import valid_bool -from ....validators.basic import valid_number -from ....validators.basic import valid_int_f1 -from ....validators.basic import valid_float_f01 -from ....validators.os import valid_abs_path -from ....validators.hw import valid_gpio_pin - -from .. import MsdError -from .. import MsdIsBusyError -from .. import MsdOfflineError -from .. import MsdConnectedError -from .. import MsdDisconnectedError -from .. import MsdMultiNotSupported -from .. import MsdCdromNotSupported -from .. import MsdRwNotSupported -from .. import BaseMsdReader -from .. import BaseMsd -from .. import MsdFileWriter - -from .gpio import Gpio - -from .drive import DeviceInfo - - -# ===== -class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes - def __init__( # pylint: disable=super-init-not-called,too-many-arguments - self, - upload_chunk_size: int, - sync_chunk_size: int, - - gpio_device_path: str, - target_pin: int, - reset_inverted: bool, - reset_pin: int, - - device_path: str, - init_delay: float, - init_retries: int, - reset_delay: float, - ) -> None: - - self.__upload_chunk_size = upload_chunk_size - self.__sync_chunk_size = sync_chunk_size - - self.__device_path = device_path - self.__init_delay = init_delay - self.__init_retries = init_retries - - self.__gpio = Gpio(gpio_device_path, target_pin, reset_pin, reset_inverted, reset_delay) - - self.__device_info: (DeviceInfo | None) = None - self.__connected = False - - self.__device_writer: (MsdFileWriter | None) = None - - self.__notifier = aiotools.AioNotifier() - self.__region = aiotools.AioExclusiveRegion(MsdIsBusyError, self.__notifier) - - @classmethod - def get_plugin_options(cls) -> dict: - return { - "upload_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)), - "sync_chunk_size": Option(4194304, type=functools.partial(valid_number, min=1024)), - - "gpio_device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="gpio_device_path"), - "target_pin": Option(-1, type=valid_gpio_pin), - "reset_pin": Option(-1, type=valid_gpio_pin), - "reset_inverted": Option(False, type=valid_bool), - - "device": Option("", type=valid_abs_path, unpack_as="device_path"), - "init_delay": Option(1.0, type=valid_float_f01), - "init_retries": Option(5, type=valid_int_f1), - "reset_delay": Option(1.0, type=valid_float_f01), - } - - def sysprep(self) -> None: - logger = get_logger(0) - self.__gpio.open() - logger.info("Using %r as MSD", self.__device_path) - try: - aiotools.run_sync(self.__load_device_info()) - except Exception as err: - log = (logger.error if isinstance(err, MsdError) else logger.exception) - log("MSD is offline: %s", err) - - async def get_state(self) -> dict: - storage: (dict | None) = None - drive: (dict | None) = None - if self.__device_info: - storage = { - "size": self.__device_info.size, - "free": self.__device_info.free, - "uploading": (self.__device_writer.get_state() if self.__device_writer else None), - } - drive = { - "image": (self.__device_info.image and dataclasses.asdict(self.__device_info.image)), - "connected": self.__connected, - } - return { - "enabled": True, - "online": bool(self.__device_info), - "busy": self.__region.is_busy(), - "storage": storage, - "drive": drive, - "features": { - "multi": False, - "cdrom": False, - "rw": False, - }, - } - - async def poll_state(self) -> AsyncGenerator[dict, None]: - prev_state: dict = {} - while True: - state = await self.get_state() - if state != prev_state: - yield state - prev_state = state - await self.__notifier.wait() - - @aiotools.atomic_fg - async def reset(self) -> None: - await aiotools.run_region_task( - "Can't reset MSD or operation was not completed", - self.__region, self.__inner_reset, - ) - - @aiotools.atomic_fg - async def __inner_reset(self) -> None: - await self.__gpio.reset() - self.__gpio.switch_to_local() - self.__connected = False - await self.__load_device_info() - get_logger(0).info("MSD reset has been successful") - - @aiotools.atomic_fg - async def cleanup(self) -> None: - try: - await self.__close_device_writer() - finally: - self.__gpio.close() - - # ===== - - @aiotools.atomic_fg - async def set_params( - self, - name: (str | None)=None, - cdrom: (bool | None)=None, - rw: (bool | None)=None, - ) -> None: - - async with self.__working(): - if name is not None: - raise MsdMultiNotSupported() - if cdrom is not None: - raise MsdCdromNotSupported() - if rw is not None: - raise MsdRwNotSupported() - - @aiotools.atomic_fg - async def set_connected(self, connected: bool) -> None: - async with self.__working(): - async with self.__region: - if connected: - if self.__connected: - raise MsdConnectedError() - self.__gpio.switch_to_server() - get_logger(0).info("MSD switched to Server") - else: - if not self.__connected: - raise MsdDisconnectedError() - self.__gpio.switch_to_local() - try: - await self.__load_device_info() - except Exception: - if self.__connected: - self.__gpio.switch_to_server() - raise - get_logger(0).info("MSD switched to KVM: %s", self.__device_info) - self.__connected = connected - - @contextlib.asynccontextmanager - async def read_image(self, name: str) -> AsyncGenerator[BaseMsdReader, None]: - async with self.__working(): - if self is not None: # XXX: Vulture and pylint hack - raise MsdMultiNotSupported() - yield BaseMsdReader() - - @contextlib.asynccontextmanager - async def write_image(self, name: str, size: int, remove_incomplete: (bool | None)) -> AsyncGenerator[MsdFileWriter, None]: - async with self.__working(): - if remove_incomplete is not None: - raise MsdMultiNotSupported() - async with self.__region: - try: - assert self.__device_info - if self.__connected: - raise MsdConnectedError() - - self.__device_writer = await MsdFileWriter( - notifier=self.__notifier, - path=self.__device_info.path, - file_size=size, - sync_size=self.__sync_chunk_size, - chunk_size=self.__upload_chunk_size, - ).open() - - await self.__write_image_info(False) - self.__notifier.notify() - yield self.__device_writer - await self.__write_image_info(True) - finally: - try: - await aiotools.shield_fg(self.__close_device_writer()) - finally: - await aiotools.shield_fg(self.__load_device_info()) - - @aiotools.atomic_fg - async def remove(self, name: str) -> None: - async with self.__working(): - raise MsdMultiNotSupported() - - # ===== - - @contextlib.asynccontextmanager - async def __working(self) -> AsyncGenerator[None, None]: - if not self.__device_info: - raise MsdOfflineError() - yield - - # ===== - - async def __write_image_info(self, complete: bool) -> None: - assert self.__device_writer - assert self.__device_info - if not (await self.__device_info.write_image_info(self.__device_writer, complete)): - get_logger().error("Can't write image info because device is full") - - async def __close_device_writer(self) -> None: - if self.__device_writer: - await self.__device_writer.close() # type: ignore - self.__device_writer = None - - async def __load_device_info(self) -> None: - retries = self.__init_retries - while True: - await asyncio.sleep(self.__init_delay) - try: - self.__device_info = await DeviceInfo.read(self.__device_path) - break - except Exception: - if retries == 0: - self.__device_info = None - raise MsdError("Can't load device info") - get_logger().exception("Can't load device info; retries=%d", retries) - retries -= 1 diff --git a/kvmd/plugins/msd/relay/drive.py b/kvmd/plugins/msd/relay/drive.py deleted file mode 100644 index 51a85392..00000000 --- a/kvmd/plugins/msd/relay/drive.py +++ /dev/null @@ -1,142 +0,0 @@ -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program 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 General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see <https://www.gnu.org/licenses/>. # -# # -# ========================================================================== # - - -import os -import stat -import fcntl -import struct -import dataclasses - -from typing import IO - -from .... import aiotools -from .... import aiofs - -from .. import MsdFileWriter - - -# ===== -_IMAGE_INFO_SIZE = 4096 -_IMAGE_INFO_MAGIC_SIZE = 16 -_IMAGE_INFO_NAME_SIZE = 256 -_IMAGE_INFO_PADS_SIZE = _IMAGE_INFO_SIZE - _IMAGE_INFO_NAME_SIZE - 1 - 8 - _IMAGE_INFO_MAGIC_SIZE * 8 -_IMAGE_INFO_FORMAT = ">%dL%dc?Q%dx%dL" % ( - _IMAGE_INFO_MAGIC_SIZE, - _IMAGE_INFO_NAME_SIZE, - _IMAGE_INFO_PADS_SIZE, - _IMAGE_INFO_MAGIC_SIZE, -) -_IMAGE_INFO_MAGIC = [0x1ACE1ACE] * _IMAGE_INFO_MAGIC_SIZE - - -# ===== [email protected](frozen=True) -class ImageInfo: - name: str - size: int - complete: bool - - @classmethod - def from_bytes(cls, data: bytes) -> ("ImageInfo" | None): - try: - parsed = list(struct.unpack(_IMAGE_INFO_FORMAT, data)) - except struct.error: - pass - else: - magic_begin = parsed[:_IMAGE_INFO_MAGIC_SIZE] - magic_end = parsed[-_IMAGE_INFO_MAGIC_SIZE:] - if magic_begin == magic_end == _IMAGE_INFO_MAGIC: - image_name_bytes = b"".join(parsed[ - _IMAGE_INFO_MAGIC_SIZE # noqa: E203 - : - _IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_NAME_SIZE - ]) - return ImageInfo( - name=image_name_bytes.decode("utf-8", errors="ignore").strip("\x00").strip(), - size=parsed[_IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_NAME_SIZE + 1], - complete=parsed[_IMAGE_INFO_MAGIC_SIZE + _IMAGE_INFO_NAME_SIZE], - ) - return None - - def to_bytes(self) -> bytes: - return struct.pack( - _IMAGE_INFO_FORMAT, - *_IMAGE_INFO_MAGIC, - *memoryview(( # type: ignore - self.name.encode("utf-8") - + b"\x00" * _IMAGE_INFO_NAME_SIZE - )[:_IMAGE_INFO_NAME_SIZE]).cast("c"), - self.complete, - self.size, - *_IMAGE_INFO_MAGIC, - ) - - [email protected](frozen=True) -class DeviceInfo: - path: str - size: int - free: int - image: (ImageInfo | None) - - @classmethod - async def read(cls, device_path: str) -> "DeviceInfo": - return (await aiotools.run_async(cls.__inner_read, device_path)) - - @classmethod - def __inner_read(cls, device_path: str) -> "DeviceInfo": - if not stat.S_ISBLK(os.stat(device_path).st_mode): - raise RuntimeError(f"Not a block device: {device_path}") - - with open(device_path, "rb") as device_file: - # size = BLKGETSIZE * BLKSSZGET - size = _ioctl_uint32(device_file, 0x1260) * _ioctl_uint32(device_file, 0x1268) - device_file.seek(size - _IMAGE_INFO_SIZE) - image_info = ImageInfo.from_bytes(device_file.read()) - - return DeviceInfo( - path=device_path, - size=size, - free=(size - image_info.size if image_info else size), - image=image_info, - ) - - async def write_image_info(self, device_writer: MsdFileWriter, complete: bool) -> bool: - device_file = device_writer.get_file() - state = device_writer.get_state() - image_info = ImageInfo(state["name"], state["written"], complete) - - if self.size - image_info.size > _IMAGE_INFO_SIZE: - await device_file.seek(self.size - _IMAGE_INFO_SIZE) # type: ignore - await device_file.write(image_info.to_bytes()) # type: ignore - await aiofs.afile_sync(device_file) - await device_file.seek(0) # type: ignore - return True - return False # Device is full - - -def _ioctl_uint32(device_file: IO, request: int) -> int: - buf = b"\0" * 4 - buf = fcntl.ioctl(device_file.fileno(), request, buf) # type: ignore - result = struct.unpack("I", buf)[0] - assert result > 0, (device_file, request, buf) - return result diff --git a/kvmd/plugins/msd/relay/gpio.py b/kvmd/plugins/msd/relay/gpio.py deleted file mode 100644 index cec31f1a..00000000 --- a/kvmd/plugins/msd/relay/gpio.py +++ /dev/null @@ -1,79 +0,0 @@ -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program 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 General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see <https://www.gnu.org/licenses/>. # -# # -# ========================================================================== # - - -import gpiod - -from .... import aiogp - - -# ===== -class Gpio: # pylint: disable=too-many-instance-attributes - def __init__( - self, - device_path: str, - target_pin: int, - reset_pin: int, - reset_inverted: bool, - reset_delay: float, - ) -> None: - - self.__device_path = device_path - self.__target_pin = target_pin - self.__reset_pin = reset_pin - self.__reset_inverted = reset_inverted - self.__reset_delay = reset_delay - - self.__chip: (gpiod.Chip | None) = None - self.__target_line: (gpiod.Line | None) = None - self.__reset_line: (gpiod.Line | None) = None - - def open(self) -> None: - assert self.__chip is None - assert self.__target_line is None - assert self.__reset_line is None - - self.__chip = gpiod.Chip(self.__device_path) - - self.__target_line = self.__chip.get_line(self.__target_pin) - self.__target_line.request("kvmd::msd::target", gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) - - self.__reset_line = self.__chip.get_line(self.__reset_pin) - self.__reset_line.request("kvmd::msd::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(self.__reset_inverted)]) - - def close(self) -> None: - if self.__chip: - try: - self.__chip.close() - except Exception: - pass - - def switch_to_local(self) -> None: - assert self.__target_line - self.__target_line.set_value(0) - - def switch_to_server(self) -> None: - assert self.__target_line - self.__target_line.set_value(1) - - async def reset(self) -> None: - assert self.__reset_line - await aiogp.pulse(self.__reset_line, self.__reset_delay, 0, self.__reset_inverted) @@ -87,7 +87,6 @@ def main() -> None: "kvmd.plugins.hid.bt", "kvmd.plugins.atx", "kvmd.plugins.msd", - "kvmd.plugins.msd.relay", "kvmd.plugins.msd.otg", "kvmd.plugins.ugpio", "kvmd.clients", diff --git a/testenv/linters/vulture-wl.py b/testenv/linters/vulture-wl.py index 0c508ec3..6a947776 100644 --- a/testenv/linters/vulture-wl.py +++ b/testenv/linters/vulture-wl.py @@ -22,6 +22,8 @@ SpiDev.no_cs SpiDev.cshigh SpiDev.max_speed_hz +_DriveImage.complete + _AtxApiPart.switch_power _UsbKey.arduino_modifier_code diff --git a/web/kvm/index.html b/web/kvm/index.html index b3b7aa65..cabe166e 100644 --- a/web/kvm/index.html +++ b/web/kvm/index.html @@ -453,21 +453,7 @@ </div> <hr> </div> - <table class="kv msd-single-storage feature-disabled"> - <tr> - <td>Current image:</td> - <td class="value" id="msd-image-name"></td> - </tr> - <tr> - <td>Image size:</td> - <td class="value" id="msd-image-size"></td> - </tr> - <tr> - <td>Storage size:</td> - <td class="value" id="msd-storage-size"></td> - </tr> - </table> - <table class="kv msd-multi-storage feature-disabled"> + <table class="kv"> <tr> <td>Image:</td> <td width="100%"> @@ -502,11 +488,9 @@ </td> </tr> </table> - <div class="msd-multi-storage feature-disabled"> - <hr> - <div class="text"> - <div class="progress" id="msd-storage-progress"><span class="progress-value" id="msd-storage-progress-value"></span></div> - </div> + <hr> + <div class="text"> + <div class="progress" id="msd-storage-progress"><span class="progress-value" id="msd-storage-progress-value"></span></div> </div> <hr> <div class="buttons buttons-row"> diff --git a/web/kvm/navbar-msd.pug b/web/kvm/navbar-msd.pug index b7ba1a75..1ffd763e 100644 --- a/web/kvm/navbar-msd.pug +++ b/web/kvm/navbar-msd.pug @@ -33,17 +33,7 @@ li(id="msd-dropdown" class="right feature-disabled") +menu_message("info", "The image is being downloaded from PiKVM") | Please wait hr - table(class="kv msd-single-storage feature-disabled") - tr - td Current image: - td(id="msd-image-name" class="value") - tr - td Image size: - td(id="msd-image-size" class="value") - tr - td Storage size: - td(id="msd-storage-size" class="value") - table(class="kv msd-multi-storage feature-disabled") + table(class="kv") tr td Image: td(width="100%") #[select(disabled id="msd-image-selector")] @@ -60,11 +50,10 @@ li(id="msd-dropdown" class="right feature-disabled") label(for="msd-mode-radio-flash") Flash td +menu_switch_notable("msd-rw-switch", "Writable", false, false, "msd-rw feature-disabled") - div(class="msd-multi-storage feature-disabled") - hr - div(class="text") - div(id="msd-storage-progress" class="progress") - span(id="msd-storage-progress-value" class="progress-value") + hr + div(class="text") + div(id="msd-storage-progress" class="progress") + span(id="msd-storage-progress-value" class="progress-value") hr div(class="buttons buttons-row") button(disabled id="msd-select-new-button" class="row50") Select image to upload diff --git a/web/share/js/kvm/msd.js b/web/share/js/kvm/msd.js index 9d454d97..8ef89dcb 100644 --- a/web/share/js/kvm/msd.js +++ b/web/share/js/kvm/msd.js @@ -236,23 +236,20 @@ export function Msd() { let s = __state; let online = (s && s.online); - $("msd-image-name").innerHTML = ((online && s.drive.image) ? s.drive.image.name : "None"); - $("msd-image-size").innerHTML = ((online && s.drive.image) ? tools.formatSize(s.drive.image.size) : "None"); if (online) { let size_str = tools.formatSize(s.storage.size); let used = s.storage.size - s.storage.free; let used_str = tools.formatSize(used); - $("msd-storage-size").innerHTML = size_str; - tools.progress.setValue($("msd-storage-progress"), `Storage: ${used_str} of ${size_str}`, used / s.storage.size * 100); + let percent = used / s.storage.size * 100; + tools.progress.setValue($("msd-storage-progress"), `Storage: ${used_str} of ${size_str}`, percent); } else { - $("msd-storage-size").innerHTML = "Unavailable"; tools.progress.setValue($("msd-storage-progress"), "Storage: unavailable", 0); } - tools.el.setEnabled($("msd-image-selector"), (online && s.features.multi && !s.drive.connected && !s.busy)); + tools.el.setEnabled($("msd-image-selector"), (online && !s.drive.connected && !s.busy)); __applyStateImageSelector(); - tools.el.setEnabled($("msd-download-button"), (online && s.features.multi && s.drive.image && !s.drive.connected && !s.busy)); - tools.el.setEnabled($("msd-remove-button"), (online && s.features.multi && s.drive.image && !s.drive.connected && !s.busy)); + tools.el.setEnabled($("msd-download-button"), (online && s.drive.image && !s.drive.connected && !s.busy)); + tools.el.setEnabled($("msd-remove-button"), (online && s.drive.image && !s.drive.connected && !s.busy)); tools.radio.setEnabled("msd-mode-radio", (online && s.features.cdrom && !s.drive.connected && !s.busy)); tools.radio.setValue("msd-mode-radio", `${Number(online && s.features.cdrom && s.drive.cdrom)}`); @@ -260,7 +257,7 @@ export function Msd() { tools.el.setEnabled($("msd-rw-switch"), (online && s.features.rw && !s.drive.connected && !s.busy)); $("msd-rw-switch").checked = (online && s.features.rw && s.drive.rw); - tools.el.setEnabled($("msd-connect-button"), (online && (!s.features.multi || s.drive.image) && !s.drive.connected && !s.busy)); + tools.el.setEnabled($("msd-connect-button"), (online && s.drive.image && !s.drive.connected && !s.busy)); tools.el.setEnabled($("msd-disconnect-button"), (online && s.drive.connected && !s.busy)); tools.el.setEnabled($("msd-select-new-button"), (online && !s.drive.connected && !__http && !s.busy)); @@ -297,12 +294,6 @@ export function Msd() { if (s) { tools.feature.setEnabled($("msd-dropdown"), s.enabled); tools.feature.setEnabled($("msd-reset-button"), s.enabled); - for (let el of $$$(".msd-single-storage")) { - tools.feature.setEnabled(el, !s.features.multi); - } - for (let el of $$$(".msd-multi-storage")) { - tools.feature.setEnabled(el, s.features.multi); - } for (let el of $$$(".msd-cdrom-emulation")) { tools.feature.setEnabled(el, s.features.cdrom); } @@ -317,13 +308,13 @@ export function Msd() { tools.hidden.setVisible($("msd-message-too-big-for-cdrom"), (online && s.features.cdrom && s.drive.cdrom && s.drive.image && s.drive.image.size >= 2359296000)); tools.hidden.setVisible($("msd-message-out-of-storage"), - (online && s.features.multi && s.drive.image && !s.drive.image.in_storage)); + (online && s.drive.image && !s.drive.image.in_storage)); tools.hidden.setVisible($("msd-message-rw-enabled"), (online && s.features.rw && s.drive.rw)); tools.hidden.setVisible($("msd-message-another-user-uploads"), (online && s.storage.uploading && !__http)); tools.hidden.setVisible($("msd-message-downloads"), - (online && s.features.multi && s.storage.downloading)); + (online && s.storage.downloading)); }; var __applyStateStatus = function() { @@ -339,7 +330,7 @@ export function Msd() { } else if (online && s.storage.uploading) { led_cls = "led-yellow-rotating-fast"; msg = "Uploading new image"; - } else if (online && s.features.multi && s.storage.downloading) { + } else if (online && s.storage.downloading) { led_cls = "led-yellow-rotating-fast"; msg = "Serving the image to download"; } else if (online) { // Sic! @@ -359,7 +350,7 @@ export function Msd() { el.options.length = 1; // Cleanup return; } - if (!s.features.multi || s.storage.uploading || s.storage.downloading) { + if (s.storage.uploading || s.storage.downloading) { return; } |