commit 9f0450b2dd0e2ec5670aa96278d7a3a843985b39
parent 40ae833af3411497de25e68cab0222a3256ec0be
Author: Daniel Barlow <dan@telent.net>
Date: Thu, 30 Apr 2026 20:41:57 +0100
wait for lua child to exit before client exits
* handle sigchld with the self-pipe trick
* use libc bindings to poll() to watch that and the socket simultaneously
Diffstat:
4 files changed, 114 insertions(+), 19 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -41,6 +41,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -83,6 +89,7 @@ dependencies = [
"fork",
"libc",
"mlua",
+ "nix",
]
[[package]]
@@ -120,6 +127,18 @@ dependencies = [
]
[[package]]
+name = "nix"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
@@ -7,3 +7,4 @@ edition = "2024"
fork = "0.7.0"
libc = "0.2.186"
mlua = { version = "0.11.6", features = ["lua53"] }
+nix = { version = "0.31.2", features = ["process"] }
diff --git a/src/bin/luac.rs b/src/bin/luac.rs
@@ -7,7 +7,8 @@ use std::{
SocketAddr
},
env,
- io::IoSlice
+ io::IoSlice,
+ process::exit
};
use std::os::linux::net::SocketAddrExt;
@@ -36,6 +37,7 @@ fn main() {
let buf = IoSlice::new(&payload);
sock.send_vectored_with_ancillary(&[buf], &mut ancillary).expect("send failed");
-
- println!("heyt");
+ let mut buf = vec![0u8; 1];
+ let _ = sock.recv(buf.as_mut_slice()).unwrap();
+ exit(buf[0].into());
}
diff --git a/src/main.rs b/src/main.rs
@@ -3,11 +3,12 @@
use mlua::Lua;
use std::{
env,
- io,
io::IoSliceMut,
+ io::Read,
path::Path,
process::exit,
- collections::HashMap
+ collections::HashMap,
+ os::fd::AsRawFd
};
use std::os::unix::net::{
@@ -18,8 +19,29 @@ use std::os::unix::net::{
};
-use libc::WEXITSTATUS;
-use fork::{Fork, fork, waitpid};
+use libc::{
+ WEXITSTATUS,
+ pollfd,
+ poll,
+ POLLIN,
+ c_void
+};
+use nix::sys::wait::{wait,WaitStatus};
+
+use fork::{Fork, fork};
+
+static mut sigchild_fd : i32 = -1;
+
+extern "C" fn handle_sigchld(_: libc::c_int) {
+ unsafe {
+ if sigchild_fd >= 0 {
+ let _ = libc::write(
+ sigchild_fd,
+ "\n".as_ptr() as * const c_void,
+ 1);
+ }
+ }
+}
fn fork_lua(lua : &Lua, args: Vec<String>, fds: Vec<i32>) -> Option<i32> {
let pathname = &args[0];
@@ -78,6 +100,14 @@ fn main() -> std::io::Result<()> {
panic!("need to set LUAD_SOCKET_PATH env var");
};
+ let Ok((mut sigchild_reader, sigchild_writer)) = std::io::pipe() else {
+ panic!("failed to open pipe for sigchild")
+ };
+
+ unsafe {
+ sigchild_fd = sigchild_writer.as_raw_fd();
+ libc::signal(libc::SIGCHLD, handle_sigchld as libc::sighandler_t);
+ };
let lua = unsafe { Lua::unsafe_new() };
let sock = UnixDatagram::bind(sock_path)?;
@@ -89,6 +119,8 @@ fn main() -> std::io::Result<()> {
// write the exit status back to the peer
// this implies we need a map of child pid -> peer address
+
+
loop {
let mut buf = [0u8; 1024];
let mut bufs = [
@@ -96,22 +128,63 @@ fn main() -> std::io::Result<()> {
];
let mut ancillary_buffer = [0; 128];
let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]);
- match sock.recv_vectored_with_ancillary_from(&mut bufs, &mut ancillary) {
- Ok((size, truncated, peer)) => {
- let fds = read_ancillary_data(ancillary);
- let args = split_command(&buf[..size-1]);
- println!("args {:?} from {:?}", args, peer);
- if let Some(child) = fork_lua(&lua, args, fds) {
- println!("forked {child}");
- peers.insert(child, peer);
- }
+ let mut pollfds = [
+ pollfd {
+ fd: sock.as_raw_fd(),
+ events: POLLIN,
+ revents: 0,
},
- Err(e) => {
- println!("error reading: {:?}", e);
+ pollfd {
+ fd: sigchild_reader.as_raw_fd(),
+ events: POLLIN,
+ revents: 0,
}
- }
+ ];
+ let n_ready = unsafe { poll(pollfds.as_mut_ptr(), 2, 10000) };
+ if n_ready > 0 {
+ println!("data! {n_ready}, {:?}, {:?}",
+ pollfds[0].revents,
+ pollfds[1].revents
+ );
+ if pollfds[0].revents > 0 {
+ match sock.recv_vectored_with_ancillary_from(&mut bufs, &mut ancillary) {
+ Ok((size, _truncated, peer)) => {
+ let fds = read_ancillary_data(ancillary);
+ let args = split_command(&buf[..size-1]);
+ println!("args {:?} from {:?}", args, peer);
+ if let Some(child) = fork_lua(&lua, args, fds) {
+ println!("forked {child}");
+ peers.insert(child, peer);
+ }
+ },
+ Err(e) => {
+ println!("error reading: {:?}", e);
+ }
+ }
+ };
+ if pollfds[1].revents > 0 {
+ println!("a child is done");
+ let mut buf = [0u8;1];
+ let _ = sigchild_reader.read(&mut buf);
+ if let Ok(wstatus) = wait() {
+ if let Some(pid) = wstatus.pid() {
+ match peers.get(&pid.as_raw()) {
+ Some(addr) => {
+ println!("send exit bytes {:?}",
+ sock.send_to_addr(b"\0\n\r\t", addr));
+ },
+ None => {
+ println!("no peer for child");
+ }
+ }
+ }
+ };
+ }
+ } else {
+ println!("timeout");
+ }
// // println!("parent {:?} {:?}", pid, result);
// match waitpid(pid) {