use crate::ported::zsh_h::{FDT_EXTERNAL, FDT_UNUSED, OPT_ARG, OPT_ISSET, module, features, options};
use std::sync::{Mutex, OnceLock};
use crate::ported::params::setiparam;
use crate::ported::utils::{addmodulefd, errflag, fdtable_get, fdtable_set, movefd, redup, zerrnam, zwarnnam};
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);
}
}