Skip to content

Commit

Permalink
pybridge: Let cockpit-beiboot find package-installed askpass helper
Browse files Browse the repository at this point in the history
Teach ensure_ferny_askpass() to first check if the system has an
installed cockpit-askpass, before it creates a new temporary file.
Turn get_libexecdir() into a proper global function for that.

This fixes running cockpit-beiboot as cockpit-wsinstance system user, as
there is no existing/writable home directory, and trying to mkdir
~/.cache EPERMs.
  • Loading branch information
martinpitt committed Sep 28, 2023
1 parent 8edda64 commit 8b3e6ce
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 17 deletions.
8 changes: 7 additions & 1 deletion src/cockpit/beiboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from cockpit.channel import ChannelRoutingRule
from cockpit.channels import PackagesChannel
from cockpit.jsonutil import JsonObject
from cockpit.packages import Packages, PackagesLoader
from cockpit.packages import Packages, PackagesLoader, get_libexecdir
from cockpit.peer import Peer
from cockpit.protocol import CockpitProblem
from cockpit.router import Router, RoutingRule
Expand All @@ -44,6 +44,12 @@


def ensure_ferny_askpass() -> Path:
# packaged case
askpass = Path(get_libexecdir(), 'cockpit-askpass')
if askpass.exists():
return askpass

# remote beipack case, create a temporary file
src_path = importlib.resources.files(ferny.__name__) / 'interaction_client.py'
src_data = src_path.read_bytes()

Expand Down
32 changes: 16 additions & 16 deletions src/cockpit/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ def sortify_version(version: str) -> str:
return '.'.join(part.zfill(8) for part in version.split('.'))


@functools.lru_cache()
def get_libexecdir() -> str:
"""Detect libexecdir on current machine
This only works for systems which have cockpit-ws installed.
"""
for candidate in ['/usr/local/libexec', '/usr/libexec', '/usr/local/lib/cockpit', '/usr/lib/cockpit']:
if os.path.exists(os.path.join(candidate, 'cockpit-askpass')):
return candidate
else:
logger.warning('Could not detect libexecdir')
# give readable error messages
return '/nonexistent/libexec'


# A document is a binary stream with a Content-Type, optional Content-Encoding,
# and optional Content-Security-Policy
class Document(NamedTuple):
Expand Down Expand Up @@ -281,21 +296,6 @@ class PackagesLoader:
'path-not-exists': lambda p: not os.path.exists(p),
}

@staticmethod
@functools.lru_cache()
def get_libexecdir() -> str:
"""Detect libexecdir on current machine
This only works for systems which have cockpit-ws installed.
"""
for candidate in ['/usr/local/libexec', '/usr/libexec', '/usr/local/lib/cockpit', '/usr/lib/cockpit']:
if os.path.exists(os.path.join(candidate, 'cockpit-askpass')):
return candidate
else:
logger.warning('Could not detect libexecdir')
# give readable error messages
return '/nonexistent/libexec'

# HACK: Type narrowing over Union types is not supported in the general case,
# but this works for the case we care about: knowing that when we pass in an
# JsonObject, we'll get an JsonObject back.
Expand All @@ -309,7 +309,7 @@ def patch_libexecdir(cls, obj: J) -> J:
abs_askpass = shutil.which('cockpit-askpass')
if abs_askpass is not None:
return obj.replace('${libexecdir}/cockpit-askpass', abs_askpass)
return obj.replace('${libexecdir}', cls.get_libexecdir())
return obj.replace('${libexecdir}', get_libexecdir())
elif isinstance(obj, dict):
return {key: cls.patch_libexecdir(value) for key, value in obj.items()}
elif isinstance(obj, list):
Expand Down

0 comments on commit 8b3e6ce

Please sign in to comment.