aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-09 13:37:20 +0100
committerJonas Maier <>2026-03-09 13:37:20 +0100
commit35dd10890d7c6d1023d6c8647c0984b69b811410 (patch)
tree3b8cf6ad62834451b8bd8a08bf5d37df6f9b2cfe
parent5cf5f57ce8aa303936fec70a61acb436825f52c7 (diff)
downloadpish-35dd10890d7c6d1023d6c8647c0984b69b811410.tar.gz
basic setup for unix socket comms
-rw-r--r--src/main.rs19
-rw-r--r--src/unix.rs136
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 }
+}