From f74eec6a1fd36f68bff9cef727c4988b0fe6d2e9 Mon Sep 17 00:00:00 2001 From: Ashley Cui Date: Thu, 24 Oct 2024 15:51:54 -0400 Subject: [PATCH] Open path in terminal Allow opening of a path from a URL option, path=path. If the terminal is busy, then warn the user and prompt them to continue. Signed-off-by: Ashley Cui --- pkg/systemd/overview.jsx | 4 +++ pkg/systemd/terminal.jsx | 73 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/pkg/systemd/overview.jsx b/pkg/systemd/overview.jsx index 7ade0dc27cb0..8300d8aabea8 100644 --- a/pkg/systemd/overview.jsx +++ b/pkg/systemd/overview.jsx @@ -168,6 +168,10 @@ class OverviewPage extends React.Component { this.state.hostnameData.OperatingSystemPrettyName &&
{cockpit.format(_("running $0"), this.state.hostnameData.OperatingSystemPrettyName)}
} + {/* DEV ARTIFACT: FOR TESTING */} +
{ show_superuser && } { "\n" } diff --git a/pkg/systemd/terminal.jsx b/pkg/systemd/terminal.jsx index 03ada383bae5..c43e3283a1d8 100644 --- a/pkg/systemd/terminal.jsx +++ b/pkg/systemd/terminal.jsx @@ -7,6 +7,11 @@ import { createRoot } from "react-dom/client"; import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect/index.js"; import { NumberInput } from "@patternfly/react-core/dist/esm/components/NumberInput/index.js"; import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from "@patternfly/react-core/dist/esm/components/Toolbar/index.js"; +import { Modal } from "@patternfly/react-core/dist/esm/components/Modal/index.js"; +import { Button } from '@patternfly/react-core/dist/esm/components/Button'; +import { Alert, AlertActionCloseButton } from "@patternfly/react-core/dist/esm/components/Alert/index.js"; + + import "./terminal.scss"; @@ -26,17 +31,21 @@ const _ = cockpit.gettext; * Spawns the user's shell in the user's home directory. */ class UserTerminal extends React.Component { - createChannel(user) { - return cockpit.channel({ + createChannel(user, dir) { + const ch = cockpit.channel({ payload: "stream", spawn: [user.shell || "/bin/bash"], environ: [ "TERM=xterm-256color", ], - directory: user.home || "/", + directory: dir || user.home || "/", pty: true, binary: true, + pid: null, }); + ch.addEventListener("ready", (_, msg) => this.setState({ pid: msg.pid }), { once: true }); + ch.addEventListener("close", () => this.setState({ pid: null }), { once: true }); + return ch; } constructor(props) { @@ -66,12 +75,17 @@ const _ = cockpit.gettext; title: 'Terminal', theme: theme || "black-theme", size: parseInt(size) || 16, + changePathBusy: false, + pid: null, + // pathError: null, }; this.onTitleChanged = this.onTitleChanged.bind(this); this.onResetClick = this.onResetClick.bind(this); this.onThemeChanged = this.onThemeChanged.bind(this); this.onPlus = this.onPlus.bind(this); this.onMinus = this.onMinus.bind(this); + this.onModal = this.onModal.bind(this); + this.onNavigate = this.onNavigate.bind(this); this.terminalRef = React.createRef(); this.resetButtonRef = React.createRef(); @@ -81,8 +95,13 @@ const _ = cockpit.gettext; } async componentDidMount() { + cockpit.addEventListener("locationchanged", this.onNavigate); const user = await cockpit.user(); - this.setState({ user, channel: this.createChannel(user) }); + this.setState({ user, channel: this.createChannel(user, cockpit.location.options.path) }); + } + + componentWillUnmount() { + cockpit.removeEventListener("locationchanged", this.onNavigate); } onTitleChanged(title) { @@ -95,6 +114,33 @@ const _ = cockpit.gettext; document.cookie = cookie; } + onModal(){ + this.setState({ changePathBusy: false }); + this.setState(prevState => ({ channel: this.createChannel(prevState.user, cockpit.location.options.path)})); + } + + async onNavigate(){ + if (cockpit.location.options.path) { + // const cmmd = "test -d " + cockpit.location.options.path + // cockpit.script(cmmd, [], {err: "message"}) + // .catch((err) => { + // this.setState({ pathError: true }) + // }) + if (this.state.pid !== null){ + const cmmd = "grep -qr '^PPid:[[:space:]]*" + this.state.pid + "$' /proc/*/status"; + cockpit.script(cmmd, [], {err: "message"}) + .then(() => { + console.log("pid exists") + this.setState({ changePathBusy: true }) + }) + .catch((err) => { + console.log("pid not exists") + this.setState(prevState => ({ channel: this.createChannel(prevState.user, cockpit.location.options.path) })); + }) + } + } + } + onPlus() { this.setState((state, _) => { localStorage.setItem('terminal:font-size', state.size + 1); @@ -185,7 +231,26 @@ const _ = cockpit.gettext; + this.setState({ changePathBusy: false })} + actions={[ + , + + ]}> + {_("There is still a process running in this terminal.\nChanging the directory will kill it.")} +
+ {/*
+ +
*/}
{terminal}