aboutsummaryrefslogtreecommitdiffstats
path: root/src/export_fun.rs
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-03-09 18:41:11 +0100
committerJonas Maier <jonas@x77.dev>2026-03-09 18:41:11 +0100
commit91250e99f113fa754f40d1d26afd774bbb25a438 (patch)
tree024b085da57503f1d4bc2d9ad426dbedbba285ad /src/export_fun.rs
parent48bb048d75c50baceba4dccd6c5dc1fff23a72fc (diff)
downloadpish-91250e99f113fa754f40d1d26afd774bbb25a438.tar.gz
correct symlinking
Diffstat (limited to 'src/export_fun.rs')
-rw-r--r--src/export_fun.rs175
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:?}");
+ }
+}