Skip to content

Commit dad877e

Browse files
authored
fix: using UnixStream as child process event signal in Unix like OS (#20)
* fix: using `UnixStream` as child process event signal in Unix like OS * Update screenshot.png * chore: clean code * Update mod.rs * Update mod.rs
1 parent ee120eb commit dad877e

File tree

5 files changed

+90
-34
lines changed

5 files changed

+90
-34
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ polling = "3"
4545
rusqlite = "0.33"
4646
rustix = "0.38"
4747
serde = "1"
48+
signal-hook = "0.3"
4849
smol = "2"
4950
thiserror = "2"
5051
tracing = "0.1"

crates/egui-term/Cargo.toml

+8-5
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ thiserror.workspace = true
2323
tracing.workspace = true
2424
wezterm-ssh = { workspace = true, features = ["vendored-openssl"] }
2525

26+
[target.'cfg(unix)'.dependencies]
27+
signal-hook.workspace = true
28+
2629
[dev-dependencies]
2730
eframe = { workspace = true, features = [
28-
"accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
29-
"default_fonts", # Embed the default egui fonts.
30-
"wgpu", # Use the glow rendering backend. Alternative: "wgpu".
31-
"persistence", # Enable restoring app state when restarting the app.
32-
] }
31+
"accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
32+
"default_fonts", # Embed the default egui fonts.
33+
"wgpu", # Use the glow rendering backend. Alternative: "wgpu".
34+
"persistence", # Enable restoring app state when restarting the app.
35+
] }

crates/egui-term/src/ssh/mod.rs

+68-27
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use alacritty_terminal::tty::{ChildEvent, EventedPty, EventedReadWrite};
55
use anyhow::Context;
66
use polling::{Event, PollMode, Poller};
77
use std::collections::HashMap;
8-
use std::net::{TcpListener, TcpStream};
98
use std::sync::Arc;
109
use tracing::{error, trace};
1110
use wezterm_ssh::{
@@ -14,10 +13,23 @@ use wezterm_ssh::{
1413
};
1514

1615
#[cfg(unix)]
17-
use std::os::fd::{AsFd, AsRawFd};
16+
use signal_hook::{
17+
consts,
18+
low_level::{pipe, unregister},
19+
SigId,
20+
};
21+
22+
#[cfg(unix)]
23+
use std::os::{
24+
fd::{AsFd, AsRawFd},
25+
unix::net::UnixStream,
26+
};
1827

1928
#[cfg(windows)]
20-
use std::os::windows::io::{AsRawSocket, AsSocket};
29+
use std::{
30+
net::{TcpListener, TcpStream},
31+
os::windows::io::{AsRawSocket, AsSocket},
32+
};
2133

2234
// Interest in PTY read/writes.
2335
#[cfg(unix)]
@@ -30,12 +42,22 @@ const PTY_CHILD_EVENT_TOKEN: usize = 1;
3042
pub struct Pty {
3143
pub pty: SshPty,
3244
pub child: SshChildProcess,
33-
pub signal: TcpStream,
45+
#[cfg(unix)]
46+
pub signals: UnixStream,
47+
#[cfg(unix)]
48+
pub sig_id: SigId,
49+
#[cfg(windows)]
50+
pub signals: TcpStream,
3451
}
3552

3653
impl Drop for Pty {
3754
fn drop(&mut self) {
3855
let _ = self.child.kill();
56+
57+
// Clear signal-hook handler.
58+
#[cfg(unix)]
59+
unregister(self.sig_id);
60+
3961
let _ = self.child.wait();
4062
}
4163
}
@@ -66,16 +88,16 @@ impl EventedReadWrite for Pty {
6688
interest.key = PTY_READ_WRITE_TOKEN;
6789
let _ = self.pty.reader.set_non_blocking(true);
6890
let _ = self.pty.writer.set_non_blocking(true);
69-
let _ = self.signal.set_nonblocking(true);
91+
let _ = self.signals.set_nonblocking(true);
7092

7193
#[cfg(unix)]
7294
{
7395
poller.add_with_mode(self.pty.reader.as_raw_fd(), interest, mode)?;
7496
poller.add_with_mode(self.pty.writer.as_raw_fd(), interest, mode)?;
7597

7698
poller.add_with_mode(
77-
self.signal.as_raw_fd(),
78-
Event::readable(PTY_CHILD_EVENT_TOKEN),
99+
&self.signals,
100+
Event::writable(PTY_CHILD_EVENT_TOKEN),
79101
PollMode::Level,
80102
)?;
81103
}
@@ -86,8 +108,8 @@ impl EventedReadWrite for Pty {
86108
poller.add_with_mode(self.pty.writer.as_raw_socket(), interest, mode)?;
87109

88110
poller.add_with_mode(
89-
self.signal.as_raw_socket(),
90-
Event::readable(crate::ssh::PTY_CHILD_EVENT_TOKEN),
111+
self.signals.as_raw_socket(),
112+
Event::readable(PTY_CHILD_EVENT_TOKEN),
91113
PollMode::Level,
92114
)?;
93115
}
@@ -109,8 +131,8 @@ impl EventedReadWrite for Pty {
109131
poller.modify_with_mode(self.pty.writer.as_fd(), interest, mode)?;
110132

111133
poller.modify_with_mode(
112-
self.signal.as_fd(),
113-
Event::readable(PTY_CHILD_EVENT_TOKEN),
134+
&self.signals,
135+
Event::writable(PTY_CHILD_EVENT_TOKEN),
114136
PollMode::Level,
115137
)?;
116138
}
@@ -121,8 +143,8 @@ impl EventedReadWrite for Pty {
121143
poller.modify_with_mode(self.pty.writer.as_socket(), interest, mode)?;
122144

123145
poller.modify_with_mode(
124-
self.signal.as_socket(),
125-
Event::readable(crate::ssh::PTY_CHILD_EVENT_TOKEN),
146+
self.signals.as_socket(),
147+
Event::readable(PTY_CHILD_EVENT_TOKEN),
126148
PollMode::Level,
127149
)?;
128150
}
@@ -136,15 +158,15 @@ impl EventedReadWrite for Pty {
136158
poller.delete(self.pty.reader.as_fd())?;
137159
poller.delete(self.pty.writer.as_fd())?;
138160

139-
poller.delete(self.signal.as_fd())?;
161+
poller.delete(&self.signals)?;
140162
}
141163

142164
#[cfg(windows)]
143165
{
144166
poller.delete(self.pty.reader.as_socket())?;
145167
poller.delete(self.pty.writer.as_socket())?;
146168

147-
poller.delete(self.signal.as_socket())?;
169+
poller.delete(self.signals.as_socket())?;
148170
}
149171

150172
Ok(())
@@ -206,10 +228,6 @@ impl Pty {
206228
verify.answer(true).await.context("send verify response")?;
207229
}
208230
SessionEvent::Authenticate(auth) => {
209-
for a in auth.prompts.iter() {
210-
println!("prompt: {}", a.prompt);
211-
}
212-
213231
let mut answers = vec![];
214232
for prompt in auth.prompts.iter() {
215233
if prompt.prompt.contains("Password") {
@@ -234,15 +252,43 @@ impl Pty {
234252

235253
// FIXME: set in settings
236254
let mut env = HashMap::new();
237-
env.insert("LANG".to_string(), "zh_CN.utf8".to_string());
255+
env.insert("LANG".to_string(), "en_US.UTF-8".to_string());
238256
env.insert("LC_COLLATE".to_string(), "C".to_string());
239257

240258
let (pty, child) = session
241259
.request_pty("xterm-256color", PtySize::default(), None, Some(env))
242260
.await?;
243261

244-
let signal = tcp_signal()?;
245-
Ok(Pty { pty, child, signal })
262+
#[cfg(unix)]
263+
{
264+
// Prepare signal handling before spawning child.
265+
let (signals, sig_id) = {
266+
let (sender, recv) = UnixStream::pair()?;
267+
268+
// Register the recv end of the pipe for SIGCHLD.
269+
let sig_id = pipe::register(consts::SIGCHLD, sender)?;
270+
recv.set_nonblocking(true)?;
271+
(recv, sig_id)
272+
};
273+
274+
Ok(Pty {
275+
pty,
276+
child,
277+
signals,
278+
sig_id,
279+
})
280+
}
281+
282+
#[cfg(windows)]
283+
{
284+
let listener = TcpListener::bind("127.0.0.1:0")?;
285+
let signals = TcpStream::connect(listener.local_addr()?)?;
286+
Ok(Pty {
287+
pty,
288+
child,
289+
signals,
290+
})
291+
}
246292
})
247293
}
248294
}
@@ -261,8 +307,3 @@ pub enum Authentication {
261307
Password(String, String),
262308
Config,
263309
}
264-
265-
fn tcp_signal() -> std::io::Result<TcpStream> {
266-
let listener = TcpListener::bind("127.0.0.1:0")?;
267-
TcpStream::connect(listener.local_addr()?)
268-
}

nxshell/assets/imgs/screenshot.png

910 Bytes
Loading

nxshell/src/ui/tab_view/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,27 @@ impl egui_dock::TabViewer for TabViewer<'_> {
7979
type Tab = Tab;
8080

8181
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
82+
let tab_id = tab.id();
8283
match &mut tab.inner {
8384
TabInner::Term(term) => match term.term_type {
8485
TermType::Ssh { ref options } => {
8586
let icon = match options.auth {
8687
Authentication::Config => DRONE,
8788
Authentication::Password(..) => NUMPAD,
8889
};
89-
format!("{icon} {}", options.name).into()
90+
if tab_id > 0 {
91+
format!("{icon} {} ({tab_id})", options.name).into()
92+
} else {
93+
format!("{icon} {}", options.name).into()
94+
}
95+
}
96+
TermType::Regular { .. } => {
97+
if tab_id > 0 {
98+
format!("local ({tab_id})").into()
99+
} else {
100+
"local".into()
101+
}
90102
}
91-
TermType::Regular { .. } => "local".into(),
92103
},
93104
TabInner::SessionList(_) => "sessions".into(),
94105
}

0 commit comments

Comments
 (0)