diff options
| author | Jonas Maier <jonas@x77.dev> | 2026-03-09 18:41:11 +0100 |
|---|---|---|
| committer | Jonas Maier <jonas@x77.dev> | 2026-03-09 18:41:11 +0100 |
| commit | 91250e99f113fa754f40d1d26afd774bbb25a438 (patch) | |
| tree | 024b085da57503f1d4bc2d9ad426dbedbba285ad /src/export_fun.rs | |
| parent | 48bb048d75c50baceba4dccd6c5dc1fff23a72fc (diff) | |
| download | pish-91250e99f113fa754f40d1d26afd774bbb25a438.tar.gz | |
correct symlinking
Diffstat (limited to 'src/export_fun.rs')
| -rw-r--r-- | src/export_fun.rs | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/export_fun.rs b/src/export_fun.rs new file mode 100644 index 0000000..934b81c --- /dev/null +++ b/src/export_fun.rs @@ -0,0 +1,175 @@ +//! allow sub-programs to invoke arbitrary user-defined functions via a unix socket + +use crate::Session; +use std::env::current_exe; +use std::ffi::OsStr; +use std::fs; +use std::io; +use std::io::Write; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::fs::symlink; +use std::os::unix::net::SocketAncillary; +use std::os::unix::net::UnixListener; +use std::os::unix::net::UnixStream; +use std::path::Path; +use std::path::PathBuf; +use std::process::exit; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; + +fn handle_server(session: Arc<Mutex<Session>>, stream: UnixStream) -> io::Result<()> { + todo!() +} + +fn handle_client(stream: UnixStream) -> io::Result<()> { + println!("uhh hi"); + todo!() +} + +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") { + if let Ok(stream) = UnixStream::connect(socket) { + let _ = handle_client(stream); + } + 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() +} + +pub struct SocketRunning { + bin_dir: PathBuf, + path: PathBuf, +} + +impl SocketRunning { + pub fn socket_path(&self) -> &Path { + &self.path + } + pub fn path(&self) -> &Path { + &self.bin_dir + } +} + +#[must_use] +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 :))"); + }; + } +} + +#[must_use] +pub fn listen(session: Arc<Mutex<Session>>) -> impl Drop { + let session_id = unique_string(); + let session_dir = crate::basedir::data_dir().join("session").join(session_id); + let bin_dir = session_dir.join("bin"); + std::fs::create_dir_all(&bin_dir).unwrap(); + let socket_path = session_dir.join("cmd.sock"); + + { + let mut se = session.lock().unwrap(); + assert!(se.socket_running.is_none()); + se.socket_running = Some(SocketRunning { + bin_dir, + path: socket_path.clone(), + }); + } + + let se = session.clone(); + thread::spawn(move || { + struct SessionRemover(PathBuf); + impl Drop for SessionRemover { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + let _session_remover = SessionRemover(session_dir); + let listener = UnixListener::bind(socket_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 { + let se = se.clone(); + thread::spawn(move || handle_server(se, stream)); + } + } + }); + + SocketDropper { session } +} + +fn create_function_hook_res( + session: Arc<Mutex<Session>>, + fun_name: &[u8], +) -> Result<(), Box<dyn std::error::Error>> { + let session = session.lock().map_err(|e| format!("{e:?}"))?; + let sock_run = session.socket_running.as_ref().ok_or("no socket running")?; + let exe_path = current_exe()?; + let symlink_path = sock_run.bin_dir.join(OsStr::from_bytes(fun_name)); + symlink(exe_path, symlink_path)?; + Ok(()) +} + +pub fn create_function_hook(session: Arc<Mutex<Session>>, fun_name: &[u8]) { + if let Err(e) = create_function_hook_res(session, fun_name) { + println!("failed to create function hook: {e:?}"); + } +} |
