use crate::ported::params::setiparam;
use crate::ported::utils::{
addmodulefd, errflag, fdtable_get, fdtable_set, movefd, redup, zerrnam, zwarnnam,
};
use crate::ported::zsh_h::{
features, module, options, FDT_EXTERNAL, FDT_UNUSED, OPT_ARG, OPT_ISSET,
};
use std::sync::{Mutex, OnceLock};
pub fn bin_zsocket(
nam: &str,
args: &[String], ops: &options,
_func: i32,
) -> i32 {
let mut soun: libc::sockaddr_un = unsafe { std::mem::zeroed() };
let mut sfd: i32;
let mut err: i32 = 1; let mut verbose = 0i32;
let mut test = 0i32;
let mut targetfd: i32 = 0;
let mut soun: libc::sockaddr_un = unsafe { std::mem::zeroed() };
let mut sfd: i32;
if OPT_ISSET(ops, b'v') {
verbose = 1;
} if OPT_ISSET(ops, b't') {
test = 1;
}
if OPT_ISSET(ops, b'd') {
let darg = OPT_ARG(ops, b'd').unwrap_or("");
targetfd = darg.parse::<i32>().unwrap_or(0); if targetfd == 0 {
zwarnnam(nam, &format!("{} is an invalid argument to -d", darg)); return 1; }
if fdtable_get(targetfd) != FDT_UNUSED {
zwarnnam(
nam, &format!("file descriptor {} is in use by the shell", targetfd),
);
return 1; } else {
}
}
if OPT_ISSET(ops, b'l') {
if args.is_empty() {
zwarnnam(nam, "-l requires an argument");
return 1; }
let localfn = args[0].as_str(); sfd = unsafe { libc::socket(libc::PF_UNIX, libc::SOCK_STREAM, 0) }; if sfd == -1 {
zwarnnam(
nam,
&format!("socket error: {} ", std::io::Error::last_os_error()),
); return 1; }
soun.sun_family = libc::AF_UNIX as _; let path_bytes = localfn.as_bytes();
let max_len = soun.sun_path.len() - 1;
let copy_len = path_bytes.len().min(max_len);
for (k, &b) in path_bytes[..copy_len].iter().enumerate() {
soun.sun_path[k] = b as libc::c_char;
}
let r = unsafe {
libc::bind(
sfd,
&soun as *const _ as *const libc::sockaddr,
size_of::<libc::sockaddr_un>() as libc::socklen_t,
)
};
if r != 0 {
zwarnnam(
nam,
&format!(
"could not bind to {}: {}",
localfn,
std::io::Error::last_os_error()
),
); unsafe {
libc::close(sfd);
} return 1; }
if unsafe { libc::listen(sfd, 1) } != 0 {
zwarnnam(
nam,
&format!(
"could not listen on socket: {}",
std::io::Error::last_os_error()
),
); unsafe {
libc::close(sfd);
} return 1; }
addmodulefd(sfd, FDT_EXTERNAL); if targetfd != 0 {
sfd = redup(sfd, targetfd); } else {
sfd = movefd(sfd); }
if sfd == -1 {
zerrnam(
nam,
&format!(
"cannot duplicate fd {}: {}",
sfd,
std::io::Error::last_os_error()
),
); return 1; }
fdtable_set(sfd, FDT_EXTERNAL); setiparam("REPLY", sfd as i64); if verbose != 0 {
println!("{} listener is on fd {}", localfn, sfd); }
return 0; } else if OPT_ISSET(ops, b'a') {
if args.is_empty() {
zwarnnam(nam, "-a requires an argument");
return 1; }
let lfd = args[0].parse::<i32>().unwrap_or(0); if lfd == 0 {
zwarnnam(nam, "invalid numerical argument");
return 1; }
if test != 0 {
let mut pfd = libc::pollfd {
fd: lfd,
events: libc::POLLIN,
revents: 0,
};
let r = unsafe { libc::poll(&mut pfd, 1, 0) }; if r == 0 {
return 1;
}
else if r == -1 {
zwarnnam(
nam,
&format!("poll error: {}", std::io::Error::last_os_error()),
); return 1; }
}
let mut len: libc::socklen_t = size_of::<libc::sockaddr_un>() as libc::socklen_t; let rfd: i32;
loop {
let r = unsafe {
libc::accept(
lfd, &mut soun as *mut _ as *mut libc::sockaddr,
&mut len,
)
};
if r >= 0 {
rfd = r;
break;
}
let osek = std::io::Error::last_os_error().raw_os_error();
if osek != Some(libc::EINTR) || errflag.load(std::sync::atomic::Ordering::Relaxed) != 0
{
rfd = r;
break;
} else {
}
}
if rfd == -1 {
zwarnnam(
nam,
&format!(
"could not accept connection: {}",
std::io::Error::last_os_error()
),
); return 1; }
addmodulefd(rfd, FDT_EXTERNAL); if targetfd != 0 {
sfd = redup(rfd, targetfd); if sfd < 0 {
zerrnam(
nam,
&format!(
"could not duplicate socket fd to {}: {}",
targetfd,
std::io::Error::last_os_error()
),
); unsafe {
libc::close(rfd);
} return 1; }
fdtable_set(sfd, FDT_EXTERNAL); } else {
sfd = rfd; }
setiparam("REPLY", sfd as i64); if verbose != 0 {
let path = soun
.sun_path
.iter()
.take_while(|&&c| c != 0)
.map(|&c| c as u8 as char)
.collect::<String>();
println!("new connection from {} is on fd {}", path, sfd); }
} else {
if args.is_empty() {
zwarnnam(nam, "zsocket requires an argument");
return 1; }
sfd = unsafe { libc::socket(libc::PF_UNIX, libc::SOCK_STREAM, 0) }; if sfd == -1 {
zwarnnam(
nam,
&format!(
"socket creation failed: {}",
std::io::Error::last_os_error()
),
); return 1; }
soun.sun_family = libc::AF_UNIX as _; let path_bytes = args[0].as_bytes();
let max_len = soun.sun_path.len() - 1;
let copy_len = path_bytes.len().min(max_len);
for (k, &b) in path_bytes[..copy_len].iter().enumerate() {
soun.sun_path[k] = b as libc::c_char;
}
err = unsafe {
libc::connect(
sfd,
&soun as *const _ as *const libc::sockaddr,
size_of::<libc::sockaddr_un>() as libc::socklen_t,
)
};
if err != 0 {
zwarnnam(
nam,
&format!("connection failed: {}", std::io::Error::last_os_error()),
); unsafe {
libc::close(sfd);
} return 1; }
addmodulefd(sfd, FDT_EXTERNAL); if targetfd != 0 {
if redup(sfd, targetfd) < 0 {
zerrnam(
nam,
&format!(
"could not duplicate socket fd to {}: {}",
targetfd,
std::io::Error::last_os_error()
),
); unsafe {
libc::close(sfd);
} return 1; }
sfd = targetfd; fdtable_set(sfd, FDT_EXTERNAL); }
setiparam("REPLY", sfd as i64); if verbose != 0 {
let path = &args[0];
println!("{} is now on fd {}", path, sfd); }
}
let _ = (err, verbose, test, targetfd); 0 }
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 {
0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 {
0 }
pub fn cleanup_(m: *const module) -> i32 {
setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0 }
static MODULE_FEATURES: OnceLock<Mutex<features>> = OnceLock::new();
fn featuresarray(_m: *const module, _f: &Mutex<features>) -> Vec<String> {
vec!["b:zsocket".to_string()]
}
fn handlefeatures(_m: *const module, _f: &Mutex<features>, enables: &mut Option<Vec<i32>>) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 1]);
}
0
}
fn setfeatureenables(_m: *const module, _f: &Mutex<features>, _e: Option<&[i32]>) -> i32 {
0
}
fn module_features() -> &'static Mutex<features> {
MODULE_FEATURES.get_or_init(|| {
Mutex::new(features {
bn_list: None,
bn_size: 1,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zsocket_l_without_arg_fails_before_socket_call() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'l' as usize] = 1;
assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
}
#[test]
fn zsocket_d_non_numeric_fails_before_dup2() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'd' as usize] = (1 << 2) | 1;
ops.args.push("not-a-number".to_string());
assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
}
fn empty_ops() -> options {
options {
ind: [0u8; crate::ported::zsh_h::MAX_OPS],
args: Vec::new(),
argscount: 0,
argsalloc: 0,
}
}
#[test]
fn zsocket_connect_mode_without_args_returns_one() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
}
#[test]
fn zsocket_a_without_arg_fails_before_accept() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'a' as usize] = 1;
assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
}
#[test]
fn zsocket_a_zero_listen_fd_fails() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'a' as usize] = 1;
assert_eq!(bin_zsocket("zsocket", &["0".to_string()], &ops, 0), 1);
assert_eq!(
bin_zsocket("zsocket", &["not-numeric".to_string()], &ops, 0),
1
);
}
#[test]
fn module_lifecycle_shims_all_return_zero() {
let _g = crate::test_util::global_state_lock();
let m = std::ptr::null();
assert_eq!(setup_(m), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
#[test]
fn features_returns_success() {
let _g = crate::test_util::global_state_lock();
let mut features = Vec::new();
assert_eq!(features_(std::ptr::null(), &mut features), 0);
}
fn ops_with_flag(flag: u8) -> options {
let mut ind = [0u8; crate::ported::zsh_h::MAX_OPS];
ind[flag as usize] = 1;
options {
ind,
args: Vec::new(),
argscount: 0,
argsalloc: 0,
}
}
#[test]
fn bin_zsocket_l_no_args_returns_one() {
let _g = crate::test_util::global_state_lock();
let ops = ops_with_flag(b'l');
let r = bin_zsocket("zsocket", &[], &ops, 0);
assert_eq!(r, 1, "-l with no args → usage error");
}
#[test]
fn bin_zsocket_no_args_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = options {
ind: [0; crate::ported::zsh_h::MAX_OPS],
args: Vec::new(),
argscount: 0,
argsalloc: 0,
};
let _ = bin_zsocket("zsocket", &[], &ops, 0);
}
#[test]
fn bin_zsocket_v_alone_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = ops_with_flag(b'v');
let _ = bin_zsocket("zsocket", &[], &ops, 0);
}
#[test]
fn bin_zsocket_d_flag_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = ops_with_flag(b'd');
let _ = bin_zsocket("zsocket", &[], &ops, 0);
}
#[test]
fn bin_zsocket_t_alone_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = ops_with_flag(b't');
let _ = bin_zsocket("zsocket", &[], &ops, 0);
}
#[test]
fn socket_setup_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(setup_(std::ptr::null()), 0);
}
#[test]
fn socket_boot_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(boot_(std::ptr::null()), 0);
}
#[test]
fn socket_cleanup_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(cleanup_(std::ptr::null()), 0);
}
#[test]
fn socket_finish_returns_zero_pin() {
let _g = crate::test_util::global_state_lock();
assert_eq!(finish_(std::ptr::null()), 0);
}
#[test]
fn bin_zsocket_return_in_exit_code_range() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
for args in [
vec![],
vec!["/tmp/zshrs_test_sock".to_string()],
vec!["".to_string()],
] {
let r = bin_zsocket("zsocket", &args, &ops, 0);
assert!(
(0..256).contains(&r),
"exit code {} must fit in u8 range for {:?}",
r,
args
);
}
}
#[test]
fn bin_zsocket_empty_path_returns_nonzero() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let r = bin_zsocket("zsocket", &["".to_string()], &ops, 0);
assert_ne!(r, 0, "empty path → error");
}
#[test]
fn bin_zsocket_no_args_deterministic() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let first = bin_zsocket("zsocket", &[], &ops, 0);
for _ in 0..5 {
assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), first);
}
}
#[test]
fn bin_zsocket_a_l_flags_with_arbitrary_arg_no_panic() {
let _g = crate::test_util::global_state_lock();
let xyz_path = std::env::temp_dir().join("__zshrs_test_zsocket_xyz");
let abc_path = std::env::temp_dir().join("__zshrs_test_zsocket_abc");
let _ = std::fs::remove_file(&xyz_path);
let _ = std::fs::remove_file(&abc_path);
let mut ops = empty_ops();
ops.ind[b'a' as usize] = 1;
let _ = bin_zsocket("zsocket", &[xyz_path.to_string_lossy().into()], &ops, 0);
let mut ops2 = empty_ops();
ops2.ind[b'l' as usize] = 1;
let _ = bin_zsocket(
"zsocket",
&[abc_path.to_string_lossy().into()],
&ops2,
0,
);
let _ = std::fs::remove_file(&xyz_path);
let _ = std::fs::remove_file(&abc_path);
}
#[test]
fn socket_full_lifecycle_returns_zero_for_all() {
let _g = crate::test_util::global_state_lock();
let null = std::ptr::null();
assert_eq!(setup_(null), 0);
let mut feats = Vec::new();
let _ = features_(null, &mut feats);
let mut enables: Option<Vec<i32>> = None;
let _ = enables_(null, &mut enables);
assert_eq!(boot_(null), 0);
assert_eq!(cleanup_(null), 0);
assert_eq!(finish_(null), 0);
}
#[test]
fn socket_setup_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(setup_(std::ptr::null()), 0);
}
}
#[test]
fn socket_finish_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(finish_(std::ptr::null()), 0);
}
}
#[test]
fn socket_cleanup_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(cleanup_(std::ptr::null()), 0);
}
}
#[test]
fn socket_boot_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(boot_(std::ptr::null()), 0);
}
}
#[test]
fn bin_zsocket_multibyte_path_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let _ = bin_zsocket("zsocket", &["/tmp/日本".to_string()], &ops, 0);
}
#[test]
fn bin_zsocket_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let _: i32 = bin_zsocket("zsocket", &[], &ops, 0);
}
#[test]
fn bin_zsocket_exit_codes_non_negative() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
for argv in [
vec![],
vec!["/tmp/sock".into()],
vec!["".into()],
vec!["/tmp/sock".into(), "extra".into()],
] {
let r = bin_zsocket("zsocket", &argv, &ops, 0);
assert!(
r >= 0,
"exit code must be non-negative, got {} for {:?}",
r,
argv
);
}
}
#[test]
fn bin_zsocket_empty_path_deterministic() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let first = bin_zsocket("zsocket", &["".to_string()], &ops, 0);
for _ in 0..5 {
assert_eq!(
bin_zsocket("zsocket", &["".to_string()], &ops, 0),
first,
"empty-path zsocket must be pure across calls"
);
}
}
#[test]
fn bin_zsocket_both_a_and_l_flags_no_panic() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'a' as usize] = 1;
ops.ind[b'l' as usize] = 1;
let _ = bin_zsocket("zsocket", &["/tmp/sock".to_string()], &ops, 0);
}
#[test]
fn bin_zsocket_d_flag_with_non_numeric_no_panic() {
let _g = crate::test_util::global_state_lock();
let mut ops = empty_ops();
ops.ind[b'd' as usize] = 1;
let _ = bin_zsocket("zsocket", &["not-a-number".to_string()], &ops, 0);
}
#[test]
fn bin_zsocket_long_path_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = empty_ops();
let path = "/tmp/".to_string() + &"x".repeat(2000);
let _ = bin_zsocket("zsocket", &[path], &ops, 0);
}
#[test]
fn socket_setup_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = setup_(std::ptr::null());
}
#[test]
fn socket_features_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let mut v: Vec<String> = Vec::new();
let _: i32 = features_(std::ptr::null(), &mut v);
}
#[test]
fn socket_enables_with_none_returns_i32() {
let _g = crate::test_util::global_state_lock();
let mut e: Option<Vec<i32>> = None;
let _: i32 = enables_(std::ptr::null(), &mut e);
}
#[test]
fn socket_enables_deterministic_for_null_in() {
let _g = crate::test_util::global_state_lock();
let mut a: Option<Vec<i32>> = None;
let first = enables_(std::ptr::null(), &mut a);
for _ in 0..3 {
let mut b: Option<Vec<i32>> = None;
assert_eq!(
enables_(std::ptr::null(), &mut b),
first,
"enables_ must be deterministic"
);
}
}
#[test]
fn socket_each_lifecycle_hook_returns_zero_individually() {
let _g = crate::test_util::global_state_lock();
let null = std::ptr::null();
let mut v: Vec<String> = Vec::new();
let mut e: Option<Vec<i32>> = None;
assert_eq!(setup_(null), 0, "c:351 setup_");
assert_eq!(features_(null, &mut v), 0, "c:358 features_");
assert_eq!(enables_(null, &mut e), 0, "c:366 enables_");
assert_eq!(boot_(null), 0, "c:373 boot_");
assert_eq!(cleanup_(null), 0, "c:380 cleanup_");
assert_eq!(finish_(null), 0, "c:387 finish_");
}
#[test]
fn socket_setup_idempotent_repeated_calls() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(setup_(std::ptr::null()), 0);
}
}
#[test]
fn socket_cleanup_idempotent_repeated_calls() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(cleanup_(std::ptr::null()), 0);
}
}
#[test]
fn socket_finish_idempotent_repeated_calls() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
assert_eq!(finish_(std::ptr::null()), 0);
}
}
#[test]
fn socket_cleanup_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = cleanup_(std::ptr::null());
}
#[test]
fn socket_finish_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = finish_(std::ptr::null());
}
#[test]
fn socket_boot_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = boot_(std::ptr::null());
}
#[test]
fn bin_zsocket_empty_args_non_negative() {
let _g = crate::test_util::global_state_lock();
let ops = crate::ported::zsh_h::options {
ind: [0u8; crate::ported::zsh_h::MAX_OPS],
args: Vec::new(),
argscount: 0,
argsalloc: 0,
};
let r = bin_zsocket("zsocket", &[], &ops, 0);
assert!(r >= 0, "bin_zsocket empty args must return ≥ 0, got {}", r);
}
#[test]
fn bin_zsocket_various_func_values_no_panic() {
let _g = crate::test_util::global_state_lock();
let ops = crate::ported::zsh_h::options {
ind: [0u8; crate::ported::zsh_h::MAX_OPS],
args: Vec::new(),
argscount: 0,
argsalloc: 0,
};
for func in [-1, 0, 1, 100, i32::MAX] {
let _ = bin_zsocket("zsocket", &[], &ops, func);
}
}
#[test]
fn bin_zsocket_deterministic_identical_args() {
let _g = crate::test_util::global_state_lock();
let ops = crate::ported::zsh_h::options {
ind: [0u8; crate::ported::zsh_h::MAX_OPS],
args: Vec::new(),
argscount: 0,
argsalloc: 0,
};
let args = vec!["test".to_string()];
let r1 = bin_zsocket("zsocket", &args, &ops, 0);
let r2 = bin_zsocket("zsocket", &args, &ops, 0);
assert_eq!(r1, r2, "bin_zsocket must be deterministic");
}
#[test]
fn socket_setup_returns_i32_type_alt_pin() {
let _g = crate::test_util::global_state_lock();
let _: i32 = setup_(std::ptr::null());
}
#[test]
fn socket_enables_with_some_non_empty_no_panic() {
let _g = crate::test_util::global_state_lock();
let mut e: Option<Vec<i32>> = Some(vec![1, 2, 3]);
let _ = enables_(std::ptr::null(), &mut e);
}
#[test]
fn socket_setup_then_boot_returns_zero_each() {
let _g = crate::test_util::global_state_lock();
let null = std::ptr::null();
assert_eq!(setup_(null), 0);
assert_eq!(boot_(null), 0);
}
}