aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-05 17:37:52 +0100
committerJonas Maier <>2026-03-05 17:37:52 +0100
commit07c802b93a241d566161d3077b3254df4067fa1e (patch)
treeebe370181c9660d3b6657d2de12e5adc3b884946
parentf919c3e5d10db7afe41a14b94b99cd38e44d2720 (diff)
downloadpish-07c802b93a241d566161d3077b3254df4067fa1e.tar.gz
lots of shenanigans to have proper execve reload
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs38
-rw-r--r--src/run/builtin.rs10
-rw-r--r--src/run/mod.rs5
5 files changed, 51 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ce9277f..e3aa48e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -12,6 +12,7 @@ checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
name = "pish"
version = "0.1.0"
dependencies = [
+ "libc",
"termios",
]
diff --git a/Cargo.toml b/Cargo.toml
index 7280820..0588542 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,4 +6,5 @@ edition = "2024"
license-file = "LICENSE"
[dependencies]
+libc = "0.2.182"
termios = "0.3"
diff --git a/src/main.rs b/src/main.rs
index 81cc5d3..6687937 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -248,6 +248,35 @@ fn event_loop() {
se.raw.disable();
}
+#[cfg(debug_assertions)]
+fn reload_shell() {
+ use std::{env, ffi::CString, ptr};
+
+ // path to this executable
+ let exe = env::current_exe().unwrap();
+ let exe_c = CString::new(exe.as_os_str().as_bytes()).unwrap();
+
+ // argv
+ let args: Vec<CString> = env::args().map(|a| CString::new(a).unwrap()).collect();
+
+ let mut argv: Vec<*const libc::c_char> = args.iter().map(|a| a.as_ptr()).collect();
+ argv.push(ptr::null());
+
+ // environment
+ let env: Vec<CString> = env::vars()
+ .map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap())
+ .collect();
+
+ let mut envp: Vec<*const libc::c_char> = env.iter().map(|e| e.as_ptr()).collect();
+ envp.push(ptr::null());
+
+ unsafe {
+ libc::execve(exe_c.as_ptr(), argv.as_ptr(), envp.as_ptr());
+ }
+
+ eprintln!("exec failed");
+}
+
fn main() {
if !io::stdin().is_terminal() {
println!("need to run in a tty");
@@ -261,7 +290,14 @@ fn main() {
let res = std::panic::catch_unwind(event_loop);
match res {
Ok(_) => break,
- Err(_) => continue,
+ Err(_) =>
+ {
+ #[cfg(debug_assertions)]
+ if run::RELOAD.load(std::sync::atomic::Ordering::SeqCst) {
+ reload_shell();
+ println!("failed to reload shell");
+ }
+ }
}
}
diff --git a/src/run/builtin.rs b/src/run/builtin.rs
index 0f90e51..da6f4f2 100644
--- a/src/run/builtin.rs
+++ b/src/run/builtin.rs
@@ -43,9 +43,13 @@ impl Builtin for re {
session.raw.disable();
match Command::new("cargo").arg("build").status() {
Ok(status) if status.success() => {
- let _ = Command::new("cargo").arg("run").status();
- session.raw.disable();
- std::process::exit(0);
+ // build of new shell succeeded
+
+ // unwind the entire stack intentionally to free resources
+ // the catch handler will check the boolean and execve into
+ // the new executable
+ super::RELOAD.store(true, std::sync::atomic::Ordering::SeqCst);
+ std::panic::resume_unwind(Box::new(42));
}
_ => (),
}
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 6484e30..0000547 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -2,12 +2,17 @@ use std::collections::HashMap;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
+#[cfg(debug_assertions)]
+use std::sync::atomic::AtomicBool;
use crate::parse::Ast;
use crate::*;
mod builtin;
+#[cfg(debug_assertions)]
+pub static RELOAD: AtomicBool = AtomicBool::new(false);
+
pub fn run(se: &mut Session, cmd: Vec<u8>) {
let parsed = parse::do_parse(&cmd);