# This file is part of Cockpit. # # Copyright (C) 2023 Red Hat, Inc. # # 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 . import logging import lzma from typing import List, Sequence, Tuple from cockpit._vendor import ferny from cockpit._vendor.bei import beipack from .data import read_cockpit_data_file from .peer import Peer, PeerError logger = logging.getLogger(__name__) def get_bridge_beipack_xz() -> Tuple[str, bytes]: try: bridge_beipack_xz = read_cockpit_data_file('cockpit-bridge.beipack.xz') logger.debug('Got pre-built cockpit-bridge.beipack.xz') except FileNotFoundError: logger.debug('Pre-built cockpit-bridge.beipack.xz; building our own.') # beipack ourselves cockpit_contents = beipack.collect_module('cockpit', recursive=True) bridge_beipack = beipack.pack(cockpit_contents, entrypoint='cockpit.bridge:main', args='beipack=True') bridge_beipack_xz = lzma.compress(bridge_beipack.encode()) logger.debug(' ... done!') return 'cockpit/data/cockpit-bridge.beipack.xz', bridge_beipack_xz class BridgeBeibootHelper(ferny.InteractionHandler): # ferny.InteractionHandler ClassVar commands = ['beiboot.provide', 'beiboot.exc'] peer: Peer payload: bytes steps: Sequence[Tuple[str, Sequence[object]]] def __init__(self, peer: Peer, args: Sequence[str] = ()) -> None: filename, payload = get_bridge_beipack_xz() self.peer = peer self.payload = payload self.steps = (('boot_xz', (filename, len(payload), tuple(args))),) async def run_command(self, command: str, args: Tuple, fds: List[int], stderr: str) -> None: logger.debug('Got ferny request %s %s %s %s', command, args, fds, stderr) if command == 'beiboot.provide': try: size, = args assert size == len(self.payload) except (AssertionError, ValueError) as exc: raise PeerError('internal-error', message=f'ferny interaction error {exc!s}') from exc assert self.peer.transport is not None logger.debug('Writing %d bytes of payload', len(self.payload)) self.peer.transport.write(self.payload) elif command == 'beiboot.exc': raise PeerError('internal-error', message=f'Remote exception: {args[0]}') else: raise PeerError('internal-error', message=f'Unexpected ferny interaction command {command}')