diff options
| author | Jonas Maier <> | 2026-03-09 13:37:20 +0100 |
|---|---|---|
| committer | Jonas Maier <> | 2026-03-09 13:37:20 +0100 |
| commit | 35dd10890d7c6d1023d6c8647c0984b69b811410 (patch) | |
| tree | 3b8cf6ad62834451b8bd8a08bf5d37df6f9b2cfe | |
| parent | 5cf5f57ce8aa303936fec70a61acb436825f52c7 (diff) | |
| download | pish-35dd10890d7c6d1023d6c8647c0984b69b811410.tar.gz | |
basic setup for unix socket comms
| -rw-r--r-- | src/main.rs | 19 | ||||
| -rw-r--r-- | src/unix.rs | 136 |
2 files changed, 151 insertions, 4 deletions
diff --git a/src/main.rs b/src/main.rs index f038336..5d2643f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(unix_socket_ancillary_data)] + use std::collections::HashMap; use std::ffi::OsStr; use std::fs; @@ -5,7 +7,7 @@ use std::io::{self, IsTerminal, Read, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::{Command, Stdio, exit}; use std::sync::{Arc, Mutex}; use std::thread::sleep; use std::time::Duration; @@ -13,6 +15,7 @@ use std::time::Duration; pub mod basedir; pub mod completion; pub mod cursor; +pub mod date; pub mod history; pub mod linebuf; pub mod panic; @@ -20,7 +23,7 @@ pub mod parse; pub mod raw; pub mod reload; pub mod run; -pub mod date; +pub mod unix; use linebuf::LineBuf; use raw::*; @@ -67,6 +70,7 @@ pub struct Session { builtins: HashMap<BString, &'static dyn run::Builtin>, vars: HashMap<BString, BString>, funs: HashMap<BString, Ast<PreExpansion>>, + socket_running: Option<unix::SocketRunning>, /// n before end of history.len() /// 0 == not checking history @@ -133,7 +137,9 @@ impl Session { let new = if self.history_visit == 0 { Vec::new() } else { - self.history[self.history.len() - self.history_visit].cmd.clone() + self.history[self.history.len() - self.history_visit] + .cmd + .clone() }; io::stdout().write_all(&new).unwrap(); io::stdout().flush().unwrap(); @@ -165,7 +171,9 @@ impl Session { } // skip it - while let Some(x) = self.line.get_left() && !x.is_ascii_whitespace() { + while let Some(x) = self.line.get_left() + && !x.is_ascii_whitespace() + { self.line.left(); i += 1; } @@ -256,6 +264,7 @@ fn event_loop() { builtins: run::builtin_map(), prev_path: vec![b'.'], history_visit: 0, + socket_running: None, vars: HashMap::new(), funs: HashMap::new(), }; @@ -263,6 +272,7 @@ fn event_loop() { print!("{}", se.prompt()); let session = Arc::new(Mutex::new(se)); + let _sock_dropper = unix::listen(session.clone()); loop { let mut buf = [0u8; 1]; @@ -479,6 +489,7 @@ fn event_loop() { } fn main() { + unix::maybe_run_defined_function(); if !io::stdin().is_terminal() { println!("need to run in a tty"); return; diff --git a/src/unix.rs b/src/unix.rs new file mode 100644 index 0000000..f26dc8c --- /dev/null +++ b/src/unix.rs @@ -0,0 +1,136 @@ +//! allow sub-programs to invoke arbitrary user-defined functions via a unix socket + +use std::ffi::OsStr; +use std::fs; +use std::io; +use std::io::Write; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::net::SocketAncillary; +use std::os::unix::net::UnixListener; +use std::os::unix::net::UnixStream; +use std::path::PathBuf; +use std::process::exit; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; + +use libc::socket; + +use crate::BString; +use crate::Session; + +fn user_function_res(sock: &OsStr) -> io::Result<()> { + let sock = UnixStream::connect(sock)?; + + let mut ancillary_buffer = [0; 128]; + let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + ancillary.add_fds(&[0, 1, 2]); + + let buf = [0; 8]; + let mut bufs = &mut [io::IoSlice::new(&buf[..])][..]; + sock.send_vectored_with_ancillary(bufs, &mut ancillary)?; + + todo!() +} + +pub fn maybe_run_defined_function() { + if let Some(program_name) = std::env::args_os().next() { + let program_name = program_name.as_bytes(); + if !program_name.contains(&b'/') && program_name != b"pish" { + if let Some(socket) = std::env::var_os("PISH_SOCKET") { + let _ = user_function_res(&socket); + exit(-1); + } + } + } +} + +fn unique_string() -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + use std::process; + use std::time::{SystemTime, UNIX_EPOCH}; + + let pid = process::id(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + + let mut hasher = DefaultHasher::new(); + pid.hash(&mut hasher); + now.hash(&mut hasher); + + let hash = hasher.finish(); + let hex = format!("{:016x}", hash); + + hex[..12.min(hex.len())].to_string() +} + +fn unique_socket_path() -> PathBuf { + let mut socket_name = b"pish-cmd-".to_vec(); + socket_name.extend_from_slice(&unique_string().as_bytes()); + socket_name.extend_from_slice(b".sock"); + let socket_path = crate::basedir::data_dir().join(OsStr::from_bytes(&socket_name)); + socket_path +} + +pub struct SocketRunning { + path: PathBuf, +} + +#[must_use] +pub struct SocketDropper { + session: Arc<Mutex<Session>>, +} + +impl Drop for SocketDropper { + fn drop(&mut self) { + // mark socket for closing by `take`ing the socket_running value + let session = &self.session; + let Ok(mut se) = session.lock() else { return }; + let Some(sr) = se.socket_running.take() else { + return; + }; + + // connect to the socket for a brief moment to make it check that it should terminate + if let Ok(mut con) = UnixStream::connect(&sr.path) { + let _ = write!(con, "please shut down :))"); + }; + } +} + +pub fn listen(session: Arc<Mutex<Session>>) -> SocketDropper { + let path = unique_socket_path(); + + { + let mut se = session.lock().unwrap(); + assert!(se.socket_running.is_none()); + se.socket_running = Some(SocketRunning { path: path.clone() }); + } + + let se = session.clone(); + thread::spawn(move || { + struct SocketRemover(PathBuf); + impl Drop for SocketRemover { + fn drop(&mut self) { + let _ = fs::remove_file(&self.0); + } + } + let _socket_remover = SocketRemover(path.clone()); + let listener = UnixListener::bind(path).unwrap(); + let mut it = listener.incoming(); + while let Some(stream) = it.next() { + match se.lock() { + Err(_) => break, + Ok(se) if se.socket_running.is_none() => break, + _ => (), + } + if let Ok(_stream) = stream { + todo!("handle client") + } + } + }); + + SocketDropper { session } +} |
