Skip to content

Commit

Permalink
Open path in terminal
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ashley-cui committed Oct 25, 2024
1 parent c36f441 commit f74eec6
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 4 deletions.
4 changes: 4 additions & 0 deletions pkg/systemd/overview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ class OverviewPage extends React.Component {
this.state.hostnameData.OperatingSystemPrettyName &&
<div className="ct-overview-header-subheading" id="system_information_os_text">{cockpit.format(_("running $0"), this.state.hostnameData.OperatingSystemPrettyName)}</div>}
</div>
{/* DEV ARTIFACT: FOR TESTING */}
<button ref={this.asdf}
className="pf-v5-c-button pf-m-secondary asdf"
onClick={() => cockpit.jump("/system/terminal#/?path=/home%2Fadmin%2Fa%2Fb")}>{_("asdf")}</button>
<div className='ct-overview-header-actions'>
{ show_superuser && <SuperuserIndicator proxy={this.superuser} /> }
{ "\n" }
Expand Down
73 changes: 69 additions & 4 deletions pkg/systemd/terminal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused imports Alert, AlertActionCloseButton.



import "./terminal.scss";

Expand All @@ -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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -185,7 +231,26 @@ const _ = cockpit.gettext;
</ToolbarItem>
</ToolbarContent>
</Toolbar>
<Modal title={_("Change directory?")}
position="top"
variant="small"
isOpen={this.state.changePathBusy}
onClose={() => this.setState({ changePathBusy: false })}
actions={[
<Button key="confirm" variant="primary" onClick={this.onModal}>
{_("Continue")}
</Button>,
<Button key="cancel" variant="secondary" onClick={() => this.setState({ changePathBusy: false })}>
{_("Cancel")}
</Button>
]}>
{_("There is still a process running in this terminal.\nChanging the directory will kill it.")}
</Modal>
</div>
{/* <div className={"warn"}>
<Alert isInline variant='danger'
title="Invalid path" />
</div> */}
<div className={"terminal-body " + this.state.theme} id="the-terminal">
{terminal}
</div>
Expand Down

0 comments on commit f74eec6

Please sign in to comment.