Skip to content

Commit cc35c44

Browse files
committed
feat: Add support for root user to connect to --user services
Removed the dedicated screen from the previous commit and instead opted for a quick local test before switching. This removes the need for the user to configure anything and it should just work out-of-the-box and still educate the user. Now also allows the application to startup in the `user` mode when called as the `root` user. Fixes #30
1 parent 659076d commit cc35c44

File tree

4 files changed

+97
-151
lines changed

4 files changed

+97
-151
lines changed

docs/customization.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,3 @@ to lower this value.
285285

286286
To view the entire output, open the output in the pager.
287287

288-
### Auto Accept Connection to `root --user` Bus
289-
290-
{{ config_block(20) }}
291-
292-
Usually this is _not_ what you should do, but I am supporting it for now.
293-

src/isd_tui/isd.py

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
Horizontal,
6767
Vertical,
6868
VerticalGroup,
69-
HorizontalGroup,
7069
)
7170

7271
from textual.reactive import reactive
@@ -295,49 +294,6 @@ def render_keybinding(inp_str: str) -> str:
295294
return "+".join(key_tokens)
296295

297296

298-
class RootUserBusModal(ModalScreen[bool]):
299-
"""
300-
Present a simple screen to inform the user that they
301-
are about to access the `root --user` service bus,
302-
which usually leads to a crash.
303-
304-
Returns their selection.
305-
"""
306-
307-
BINDINGS = [
308-
Binding("escape", "close", "Close", show=True),
309-
]
310-
AUTO_FOCUS = "#no"
311-
312-
def __init__(
313-
self,
314-
**kwargs,
315-
):
316-
super().__init__(**kwargs)
317-
318-
def compose(self) -> ComposeResult:
319-
with VerticalGroup():
320-
yield Markdown(
321-
"# About to access **root --user** bus\n"
322-
+ "Usually the `root` user does _not_ have a dedicated `--user` bus. "
323-
+ "The application will crash if you try to connect to a non-existing bus. "
324-
+ "Are you sure you want to connect to it?\n\n"
325-
+ "You can update the settings to auto-accept this question if this is what you want."
326-
)
327-
with HorizontalGroup():
328-
yield Button("No", id="no", variant="primary")
329-
yield Button("Yes", id="yes", variant="warning")
330-
yield Footer()
331-
332-
def action_close(self) -> None:
333-
self.dismiss(False)
334-
335-
def on_button_pressed(self, event: Button.Pressed) -> None:
336-
if event.button.id == "no":
337-
self.dismiss(False)
338-
self.dismiss(True)
339-
340-
341297
class SystemctlActionScreen(ModalScreen[Optional[str]]):
342298
"""
343299
Present a screen with the configured systemctl actions.
@@ -923,15 +879,6 @@ class Settings(BaseSettings):
923879
Note: The output is not trimmed when a pager or editor is opened!"""),
924880
)
925881

926-
auto_accept_connection_to_root_user_bus: bool = Field(
927-
default=False,
928-
description=dedent("""\
929-
By default, the user is asked if they actually want to connect
930-
to the `root --user` bus, as this usually crashes the running program.
931-
If this option is `True`, the question is skipped.
932-
"""),
933-
)
934-
935882
# https://github.com/tinted-theming/home?tab=readme-ov-file
936883
@classmethod
937884
def settings_customise_sources(
@@ -1818,20 +1765,25 @@ def compose(self) -> ComposeResult:
18181765
)
18191766

18201767

1821-
# FUTURE: Maybe call it 'allowed' mode?
18221768
def derive_startup_mode(startup_mode: StartupMode) -> str:
1823-
if is_root():
1824-
# if user == `root` then only system is allowed
1825-
return "system"
1769+
mode: str
18261770
if startup_mode == StartupMode("auto"):
18271771
fallback: StartupMode = StartupMode("user")
18281772
fp = get_isd_cached_state_json_file_path()
18291773
if fp.exists():
1830-
return json.loads(fp.read_text()).get("mode", fallback)
1774+
mode = json.loads(fp.read_text()).get("mode", fallback)
18311775
else:
1832-
return fallback
1776+
mode = fallback
18331777
else:
1834-
return startup_mode
1778+
mode = startup_mode
1779+
if mode == "user" and is_root():
1780+
if systemctl_is_system_running(mode="user").returncode == 0:
1781+
return "user"
1782+
else:
1783+
# If it is not possible to connect to the `root` users `--user` bus,
1784+
# fallback to system
1785+
return "system"
1786+
return mode
18351787

18361788

18371789
def cached_search_term() -> str:
@@ -1841,6 +1793,14 @@ def cached_search_term() -> str:
18411793
return ""
18421794

18431795

1796+
def systemctl_is_system_running(*, mode: str) -> subprocess.CompletedProcess[str]:
1797+
return subprocess.run(
1798+
systemctl_args_builder("is-system-running", mode=mode, units=[]),
1799+
capture_output=True,
1800+
text=True,
1801+
)
1802+
1803+
18441804
# class SettingsError(ModalScreen):
18451805
# def __init__(
18461806
# self, settings: Settings, exception: Exception, *args, **kwargs
@@ -2183,20 +2143,7 @@ def action_copy_unit_path(self) -> None:
21832143
self.app.copy_to_clipboard(path)
21842144
self.notify(f"Copied '{path}' to the clipboard.")
21852145

2186-
async def wants_root_user_bus(self) -> bool:
2187-
"""
2188-
Ask the user if they know what they actually want to connect to the
2189-
user-bus of the `root` user.
2190-
"""
2191-
prev_focus = self.focused
2192-
self.set_focus(None)
2193-
do_switch = await self.app.push_screen_wait(RootUserBusModal())
2194-
self.set_focus(prev_focus)
2195-
return do_switch
2196-
2197-
# `work` is required to push the screen for `wants_root_user_bus`.
2198-
@work
2199-
async def action_toggle_mode(self) -> None:
2146+
def action_toggle_mode(self) -> None:
22002147
"""
22012148
Toggle the current `bus`.
22022149
Try to protect the user from accidentally crashing the program by trying
@@ -2205,12 +2152,18 @@ async def action_toggle_mode(self) -> None:
22052152
22062153
- <https://github.com/isd-project/isd/issues/30>
22072154
"""
2208-
if (
2209-
is_root()
2210-
and self.mode == "system"
2211-
and not self.settings.auto_accept_connection_to_root_user_bus
2212-
):
2213-
if not (await self.wants_root_user_bus()):
2155+
if is_root() and self.mode == "system":
2156+
# Test if `root` user can actually connect to a `--user` bus.
2157+
proc = systemctl_is_system_running(mode="user")
2158+
if proc.returncode != 0:
2159+
self.notify(
2160+
"Could not connect to `root` users `--user` bus.\n"
2161+
+ "Usually, this does not work, as the `root` user does not have any `user` services.\n"
2162+
+ "The connection was tested via `systemctl --user is-system-running` as `root` user with the full error message below:\n\n"
2163+
+ proc.stderr,
2164+
severity="error",
2165+
timeout=60,
2166+
)
22142167
return
22152168

22162169
self.mode = "system" if self.mode == "user" else "user"

0 commit comments

Comments
 (0)