T32: on Windows herdr's client is sought among the herdr.exe processes at or under the session's shell, none under another herdr, whose server and panes are its own; they are read when that list changes, and a server or a command beside the client is passed by, where round 1 found the server taken for the client
This commit is contained in:
5 files changed
+120
-63
No files matched your search
+47
-31
@@ -30,46 +30,43 @@ use crate::term::Pty;
|
||||
|
||||
pub(super) type Pid = u32;
|
||||
|
||||
/// The herdr processes at or under the session's shell, nearest first.
|
||||
pub(super) type Program = Vec<Pid>;
|
||||
|
||||
/// herdr's executable.
|
||||
pub(super) const HERDR: &str = "herdr.exe";
|
||||
const HERDR: &str = "herdr.exe";
|
||||
|
||||
/// The session's shell: Windows has no foreground group, so herdr is sought under it.
|
||||
pub(super) fn leader(pty: &Pty) -> io::Result<Pid> {
|
||||
Ok(pty.pid)
|
||||
}
|
||||
|
||||
/// The herdr process nearest under `shell`, else `shell`; each with its executable's name.
|
||||
pub(super) fn running(shell: Pid) -> io::Result<(Pid, Option<PathBuf>)> {
|
||||
let processes = processes()?;
|
||||
let found = nearest(&processes, shell, HERDR)
|
||||
.or_else(|| processes.iter().find(|(pid, ..)| *pid == shell));
|
||||
Ok(found.map_or((shell, None), |(pid, _, name)| {
|
||||
(*pid, Some(PathBuf::from(name)))
|
||||
}))
|
||||
pub(super) fn running(shell: Pid) -> io::Result<Program> {
|
||||
Ok(herdrs(&processes()?, shell))
|
||||
}
|
||||
|
||||
/// The process nearest under `root` that runs `name`, breadth first.
|
||||
pub(super) fn nearest<'a>(
|
||||
processes: &'a [(Pid, Pid, String)],
|
||||
root: Pid,
|
||||
name: &str,
|
||||
) -> Option<&'a (Pid, Pid, String)> {
|
||||
let mut under = vec![root];
|
||||
let mut at = 0;
|
||||
while let Some(&parent) = under.get(at) {
|
||||
/// The herdr processes at or under `root`, breadth first; what a herdr starts, its server among them, is its own.
|
||||
pub(super) fn herdrs(processes: &[(Pid, Pid, String)], root: Pid) -> Vec<Pid> {
|
||||
let herdr = |pid: Pid| {
|
||||
processes
|
||||
.iter()
|
||||
.any(|(id, _, name)| *id == pid && name.eq_ignore_ascii_case(HERDR))
|
||||
};
|
||||
let (mut under, mut found, mut at) = (vec![root], Vec::new(), 0);
|
||||
while let Some(&pid) = under.get(at) {
|
||||
at += 1;
|
||||
for process in processes.iter().filter(|(_, of, _)| *of == parent) {
|
||||
if herdr(pid) {
|
||||
found.push(pid);
|
||||
continue;
|
||||
}
|
||||
for (child, ..) in processes.iter().filter(|(_, of, _)| *of == pid) {
|
||||
// A parent id outlives its process, so a stale one may lead back up.
|
||||
if under.contains(&process.0) {
|
||||
continue;
|
||||
if !under.contains(child) {
|
||||
under.push(*child);
|
||||
}
|
||||
if process.2.eq_ignore_ascii_case(name) {
|
||||
return Some(process);
|
||||
}
|
||||
under.push(process.0);
|
||||
}
|
||||
}
|
||||
None
|
||||
found
|
||||
}
|
||||
|
||||
/// Every process: its id, its parent's, and its executable's name.
|
||||
@@ -107,11 +104,30 @@ pub(super) fn processes() -> io::Result<Vec<(Pid, Pid, String)>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The socket of the herdr server whose terminal client is process `pid`, if it is one.
|
||||
pub(super) fn client(pid: Pid) -> anyhow::Result<Option<PathBuf>> {
|
||||
let (args, env) = launch(pid).with_context(|| format!("herdr: process {pid} does not read"))?;
|
||||
// herdr names its pipe after the socket's path as written, relative or not.
|
||||
follows(&args, &env)
|
||||
/// The socket of the herdr server whose terminal client is among `herdrs`; a server or a command is passed by.
|
||||
pub(super) fn client(herdrs: Program) -> anyhow::Result<Option<PathBuf>> {
|
||||
pick(herdrs.into_iter().map(|pid| (pid, launch(pid))))
|
||||
}
|
||||
|
||||
/// The first terminal client's socket in `reads`; with none, a failed read is the error, so it is tried again.
|
||||
pub(super) fn pick(
|
||||
reads: impl IntoIterator<Item = (Pid, io::Result<Invocation>)>,
|
||||
) -> anyhow::Result<Option<PathBuf>> {
|
||||
let mut unread = None;
|
||||
for (pid, read) in reads {
|
||||
match read.with_context(|| format!("herdr: process {pid} does not read")) {
|
||||
// herdr names its pipe after the socket's path as written, relative or not.
|
||||
Ok((args, env)) => {
|
||||
if let Some(socket) = follows(&args, &env)? {
|
||||
return Ok(Some(socket));
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
unread.get_or_insert(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
unread.map_or(Ok(None), Err)
|
||||
}
|
||||
|
||||
/// The arguments and variables of process `pid`; names fold to upper case, as Windows matches them.
|
||||
|
||||
Reference in new issue
Block a user