luad

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
MCargo.lock | 19+++++++++++++++++++
MCargo.toml | 1+
Msrc/bin/luac.rs | 8+++++---
Msrc/main.rs | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
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) {