File: //lib/python3.9/site-packages/cockpit/beipack.py
# 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 <https://www.gnu.org/licenses/>.
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}')