#![cfg(unix)]
use mlua::{Lua, Result as LuaResult, Table, Value, MultiValue};
use crate::posix::*;
use crate::lfs;
use std::sync::atomic::{AtomicBool, Ordering};
static HAVE_FORKED: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "linux")]
unsafe fn errno_location() -> *mut libc::c_int {
libc::__errno_location()
}
#[cfg(target_os = "macos")]
unsafe fn errno_location() -> *mut libc::c_int {
libc::__error()
}
pub(crate) fn pushresult(lua: &Lua, result: i32, info: Option<&str>) -> LuaResult<MultiValue> {
if result != -1 {
let mut ret = MultiValue::new();
ret.push_front(Value::Integer(result as i64));
Ok(ret)
} else {
pusherror(lua, info)
}
}
pub(crate) fn pusherror(lua: &Lua, info: Option<&str>) -> LuaResult<MultiValue> {
pusherror_with_code(lua, info, None)
}
pub(crate) fn pusherror_with_code(lua: &Lua, info: Option<&str>, code: Option<i32>) -> LuaResult<MultiValue> {
let error_code = code.unwrap_or_else(|| unsafe { *errno_location() });
let error_string = if let Some(i) = info {
if code.is_none() {
let err_msg = unsafe {
let c_str = libc::strerror(error_code);
std::ffi::CStr::from_ptr(c_str).to_string_lossy().to_string()
};
format!("{}: {}", i, err_msg)
} else {
i.to_string()
}
} else {
let err_msg = unsafe {
let c_str = libc::strerror(error_code);
std::ffi::CStr::from_ptr(c_str).to_string_lossy().to_string()
};
err_msg
};
let mut ret = MultiValue::new();
ret.push_front(Value::Integer(error_code as i64));
ret.push_front(Value::String(lua.create_string(&error_string)?));
ret.push_front(Value::Nil);
Ok(ret)
}
pub fn register_posix_extensions(lua: &Lua) -> LuaResult<()> {
let mut posix_table = lua.create_table()?;
register_simple_posix_functions(lua, &mut posix_table)?;
register_file_posix_functions(lua, &mut posix_table)?;
register_env_posix_functions(lua, &mut posix_table)?;
register_system_posix_functions(lua, &mut posix_table)?;
lua.globals().set("posix", posix_table)?;
Ok(())
}
fn register_simple_posix_functions(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
macro_rules! posix_bind1 {
($name:literal, $p1:ident, $expr:expr) => {
posix_table.set($name, lua.create_function(|lua, $p1: String| -> LuaResult<MultiValue> {
let result = match $expr {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, Some(&$p1))
})?)?;
};
}
macro_rules! posix_bind2 {
($name:literal, $p1:ident, $p2:ident, $expr:expr) => {
posix_table.set($name, lua.create_function(|lua, ($p1, $p2): (String, String)| -> LuaResult<MultiValue> {
let result = match $expr {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, None)
})?)?;
};
}
macro_rules! posix_bind_string0 {
($name:literal, $expr:expr) => {
posix_table.set($name, lua.create_function(|lua, ()| -> LuaResult<mlua::Value> {
match $expr {
Some(s) => Ok(mlua::Value::String(lua.create_string(&s)?)),
None => Ok(mlua::Value::Nil),
}
})?)?;
};
}
macro_rules! posix_bind_string1 {
($name:literal, $p1:ident, $default:expr, $expr:expr) => {
posix_table.set($name, lua.create_function(|lua, $p1: Option<i32>| -> LuaResult<mlua::Value> {
let $p1 = $p1.unwrap_or($default);
match $expr {
Some(s) => Ok(mlua::Value::String(lua.create_string(&s)?)),
None => Ok(mlua::Value::Nil),
}
})?)?;
};
}
posix_bind1!("mkdir", path, posix_mkdir(&path));
posix_bind1!("mkfifo", path, posix_mkfifo(&path));
posix_bind1!("rmdir", path, lfs::remove_dir(&path));
posix_bind1!("unlink", path, lfs::remove_file(&path));
posix_bind2!("link", oldpath, newpath, lfs::hard_link(&oldpath, &newpath));
posix_bind2!("symlink", oldpath, newpath, lfs::symlink_for_native(&oldpath, &newpath));
posix_bind1!("chdir", path, std::env::set_current_dir(&path));
posix_bind_string0!("getlogin", posix_getlogin());
posix_bind_string1!("ttyname", fd, 0, posix_ttyname(fd));
Ok(())
}
fn register_file_posix_functions(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
posix_table.set("access", lua.create_function(|lua, (path, mode): (String, Option<String>)| -> LuaResult<MultiValue> {
let mode_str = mode.as_deref().unwrap_or("f");
match posix_access(&path, mode_str) {
Ok(true) => pushresult(lua, 0, Some(&path)),
Ok(false) => pushresult(lua, -1, Some(&path)),
Err(_e) => pushresult(lua, -1, Some(&path)),
}
})?)?;
posix_table.set("chmod", lua.create_function(|lua, (path, mode): (String, String)| -> LuaResult<MultiValue> {
let result = match posix_chmod(&path, &mode) {
Ok(()) => 0,
Err(_) => {
if mode == "-w" {
0
} else {
-1
}
},
};
pushresult(lua, result, Some(&path))
})?)?;
posix_table.set("chown", lua.create_function(|lua, (path, uid, gid): (String, mlua::Value, mlua::Value)| -> LuaResult<MultiValue> {
let uid_str = match uid {
mlua::Value::String(s) => Some(s.to_str()?.to_string()),
mlua::Value::Number(n) => Some(n.to_string()),
mlua::Value::Integer(i) => Some(i.to_string()),
mlua::Value::Nil => None,
_ => return Err(mlua::Error::RuntimeError("uid must be string or number".to_string())),
};
let gid_str = match gid {
mlua::Value::String(s) => Some(s.to_str()?.to_string()),
mlua::Value::Number(n) => Some(n.to_string()),
mlua::Value::Integer(i) => Some(i.to_string()),
mlua::Value::Nil => None,
_ => return Err(mlua::Error::RuntimeError("gid must be string or number".to_string())),
};
let result = match posix_chown(&path, uid_str.as_deref(), gid_str.as_deref()) {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, Some(&path))
})?)?;
posix_table.set("umask", lua.create_function(|lua, mask: Option<String>| -> LuaResult<mlua::Value> {
let mask_str = mask.as_deref();
match posix_umask(mask_str) {
Ok(mode_str) => Ok(mlua::Value::String(lua.create_string(&mode_str)?)),
Err(_) => {
Ok(mlua::Value::Nil)
}
}
})?)?;
{
use std::fs;
posix_table.set("readlink", lua.create_function(|lua, path: String| -> LuaResult<MultiValue> {
match fs::read_link(&path) {
Ok(link_path) => {
let mut ret = MultiValue::new();
ret.push_front(Value::String(lua.create_string(&*link_path.to_string_lossy())?));
Ok(ret)
}
Err(_) => pusherror(lua, Some(&path)),
}
})?)?;
}
posix_table.set("getcwd", lua.create_function(|lua, (): ()| -> LuaResult<MultiValue> {
match std::env::current_dir() {
Ok(cwd) => {
let mut ret = MultiValue::new();
ret.push_front(Value::String(lua.create_string(&*cwd.to_string_lossy())?));
Ok(ret)
}
Err(_) => pusherror(lua, Some("getcwd")),
}
})?)?;
posix_table.set("stat", lua.create_function(|lua, (path, selector): (String, Option<String>)| -> LuaResult<mlua::Value> {
let stat = match posix_stat(&path) {
Ok(s) => s,
Err(_) => {
return Ok(mlua::Value::Nil);
}
};
if let Some(sel) = selector {
match sel.as_str() {
"mode" => Ok(mlua::Value::String(lua.create_string(&stat.mode_str)?)),
"ino" => Ok(mlua::Value::Integer(stat.ino as i64)),
"dev" => Ok(mlua::Value::Integer(stat.dev as i64)),
"nlink" => Ok(mlua::Value::Integer(stat.nlink as i64)),
"uid" => Ok(mlua::Value::Integer(stat.uid as i64)),
"gid" => Ok(mlua::Value::Integer(stat.gid as i64)),
"size" => Ok(mlua::Value::Integer(stat.size as i64)),
"atime" => Ok(mlua::Value::Integer(stat.atime as i64)),
"mtime" => Ok(mlua::Value::Integer(stat.mtime as i64)),
"ctime" => Ok(mlua::Value::Integer(stat.ctime as i64)),
"type" => Ok(mlua::Value::String(lua.create_string(&stat.file_type)?)),
"_mode" => Ok(mlua::Value::Integer(stat.mode as i64)),
_ => Err(mlua::Error::RuntimeError(format!("unknown stat selector: {}", sel))),
}
} else {
let stat_table = lua.create_table()?;
stat_table.set("mode", stat.mode_str.as_str())?;
stat_table.set("ino", stat.ino as i64)?;
stat_table.set("dev", stat.dev as i64)?;
stat_table.set("nlink", stat.nlink as i64)?;
stat_table.set("uid", stat.uid as i64)?;
stat_table.set("gid", stat.gid as i64)?;
stat_table.set("size", stat.size as i64)?;
stat_table.set("atime", stat.atime as i64)?;
stat_table.set("mtime", stat.mtime as i64)?;
stat_table.set("ctime", stat.ctime as i64)?;
stat_table.set("type", stat.file_type.as_str())?;
stat_table.set("_mode", stat.mode as i64)?;
Ok(mlua::Value::Table(stat_table))
}
})?)?;
posix_table.set("mkstemp", lua.create_function(|lua, template: String| -> LuaResult<MultiValue> {
let (path, _file) = match posix_mkstemp(&template) {
Ok((p, f)) => (p, f),
Err(_) => return pusherror(lua, Some(&template)),
};
let mut ret = MultiValue::new();
ret.push_front(Value::Nil);
ret.push_front(Value::String(lua.create_string(&path)?));
Ok(ret)
})?)?;
posix_table.set("utime", lua.create_function(|lua, (path, mtime, atime): (String, Option<u64>, Option<u64>)| -> LuaResult<MultiValue> {
let result = match posix_utime(&path, mtime, atime) {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, Some(&path))
})?)?;
posix_table.set("setuid", lua.create_function(|lua, name_or_id: mlua::Value| -> LuaResult<MultiValue> {
let uid = match name_or_id {
mlua::Value::String(s) => {
let name = s.to_str()?.to_string();
match posix_getuid_by_name(&name) {
Some(u) => u,
None => {
return pushresult(lua, unsafe { libc::setuid(!0) }, None);
}
}
}
mlua::Value::Number(n) => n as i64 as u32,
mlua::Value::Integer(i) => i as u32,
_ => return Err(mlua::Error::RuntimeError("setuid: argument must be string or number".to_string())),
};
let result = match posix_setuid(uid) {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, None)
})?)?;
posix_table.set("setgid", lua.create_function(|lua, name_or_id: mlua::Value| -> LuaResult<MultiValue> {
let gid = match name_or_id {
mlua::Value::String(s) => {
let name = s.to_str()?.to_string();
match posix_getgid_by_name(&name) {
Some(g) => g,
None => {
return pushresult(lua, unsafe { libc::setgid(!0) }, None);
}
}
}
mlua::Value::Number(n) => n as i64 as u32,
mlua::Value::Integer(i) => i as u32,
_ => return Err(mlua::Error::RuntimeError("setgid: argument must be string or number".to_string())),
};
let result = match posix_setgid(gid) {
Ok(()) => 0,
Err(_) => -1,
};
pushresult(lua, result, None)
})?)?;
Ok(())
}
fn register_posix_env_var_bindings(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
posix_table.set("getenv", lua.create_function(|lua, name: Option<String>| -> LuaResult<mlua::Value> {
if let Some(n) = name {
match std::env::var(&n) {
Ok(val) => Ok(mlua::Value::String(lua.create_string(&val)?)),
Err(std::env::VarError::NotPresent) => Ok(mlua::Value::Nil),
Err(e) => Err(mlua::Error::RuntimeError(format!("getenv {}: {}", n, e))),
}
} else {
let env_table = lua.create_table()?;
for (key, value) in std::env::vars() {
env_table.set(key, value)?;
}
Ok(mlua::Value::Table(env_table))
}
})?)?;
posix_table.set("putenv", lua.create_function(|lua, env_str: String| -> LuaResult<MultiValue> {
let env_cstr = std::ffi::CString::new(env_str.clone())
.map_err(|_| mlua::Error::RuntimeError("putenv: string contains null byte".to_string()))?;
let leaked = Box::leak(Box::new(env_cstr));
let result = unsafe { libc::putenv(leaked.as_ptr() as *mut libc::c_char) };
pushresult(lua, result, Some(&env_str))
})?)?;
posix_table.set("setenv", lua.create_function(|lua, (name, value, overwrite): (String, String, Option<bool>)| -> LuaResult<MultiValue> {
let overwrite = overwrite.unwrap_or(true);
let name_cstr = std::ffi::CString::new(&*name)
.map_err(|_| mlua::Error::RuntimeError("setenv: name contains null byte".to_string()))?;
let value_cstr = std::ffi::CString::new(&*value)
.map_err(|_| mlua::Error::RuntimeError("setenv: value contains null byte".to_string()))?;
let result = unsafe { libc::setenv(name_cstr.as_ptr(), value_cstr.as_ptr(), overwrite as i32) };
pushresult(lua, result, Some(&name))
})?)?;
posix_table.set("unsetenv", lua.create_function(|_lua, name: String| -> LuaResult<MultiValue> {
std::env::remove_var(&name);
let mut ret = MultiValue::new();
ret.push_front(Value::Integer(0));
Ok(ret)
})?)?;
Ok(())
}
fn register_posix_passwd_group_bindings(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
posix_table.set("getpasswd", lua.create_function(|lua, (name_or_id, selector): (Option<mlua::Value>, Option<String>)| -> LuaResult<mlua::Value> {
let passwd = match name_or_id {
None => posix_getpasswd(None, None),
Some(mlua::Value::String(s)) => {
let name = s.to_str()?.to_string();
posix_getpasswd(Some(&name), None)
}
Some(mlua::Value::Number(n)) => {
let uid = n as i64 as u32;
posix_getpasswd(None, Some(uid))
}
Some(mlua::Value::Integer(i)) => {
let uid = i as u32;
posix_getpasswd(None, Some(uid))
}
_ => return Err(mlua::Error::RuntimeError("getpasswd: argument must be string or number".to_string())),
};
match passwd {
Ok(pw) => {
if let Some(sel) = selector {
match sel.as_str() {
"name" => Ok(mlua::Value::String(lua.create_string(&pw.name)?)),
"uid" => Ok(mlua::Value::Integer(pw.uid as i64)),
"gid" => Ok(mlua::Value::Integer(pw.gid as i64)),
"dir" => Ok(mlua::Value::String(lua.create_string(&pw.dir)?)),
"shell" => Ok(mlua::Value::String(lua.create_string(&pw.shell)?)),
"gecos" => Ok(mlua::Value::String(lua.create_string(&pw.gecos)?)),
"passwd" => Ok(mlua::Value::String(lua.create_string(&pw.passwd)?)),
_ => Err(mlua::Error::RuntimeError(format!("unknown getpasswd selector: {}", sel))),
}
} else {
let pw_table = lua.create_table()?;
pw_table.set("name", pw.name)?;
pw_table.set("uid", pw.uid as i64)?;
pw_table.set("gid", pw.gid as i64)?;
pw_table.set("dir", pw.dir)?;
pw_table.set("shell", pw.shell)?;
pw_table.set("gecos", pw.gecos)?;
pw_table.set("passwd", pw.passwd)?;
Ok(mlua::Value::Table(pw_table))
}
}
Err(_) => Ok(mlua::Value::Nil),
}
})?)?;
posix_table.set("getgroup", lua.create_function(|lua, name_or_id: Option<mlua::Value>| {
let group = match name_or_id {
None => return Err(mlua::Error::RuntimeError("getgroup: requires name or gid".to_string())),
Some(mlua::Value::String(s)) => {
let name = s.to_str()?.to_string();
posix_getgroup(Some(&name), None)
}
Some(mlua::Value::Number(n)) => {
let gid = n as i64 as u32;
posix_getgroup(None, Some(gid))
}
Some(mlua::Value::Integer(i)) => {
let gid = i as u32;
posix_getgroup(None, Some(gid))
}
_ => return Err(mlua::Error::RuntimeError("getgroup: argument must be string or number".to_string())),
};
match group {
Ok(gr) => {
let gr_table = lua.create_table()?;
gr_table.set("name", gr.name)?;
gr_table.set("gid", gr.gid as i64)?;
for (i, member) in gr.members.iter().enumerate() {
gr_table.set(i + 1, member.as_str())?;
}
Ok(mlua::Value::Table(gr_table))
}
Err(_) => Ok(mlua::Value::Nil),
}
})?)?;
Ok(())
}
fn register_env_posix_functions(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
register_posix_env_var_bindings(lua, posix_table)?;
register_posix_passwd_group_bindings(lua, posix_table)?;
Ok(())
}
fn register_system_posix_functions(lua: &Lua, posix_table: &mut Table) -> LuaResult<()> {
posix_table.set("sleep", lua.create_function(|_lua, seconds: u32| {
let remaining = unsafe { libc::sleep(seconds) };
Ok(remaining as i64)
})?)?;
posix_table.set("getprocessid", lua.create_function(|lua, selector: Option<String>| -> LuaResult<mlua::Value> {
if let Some(sel) = selector {
let id = match sel.as_str() {
"egid" => unsafe { libc::getegid() as i64 },
"euid" => unsafe { libc::geteuid() as i64 },
"gid" => unsafe { libc::getgid() as i64 },
"uid" => unsafe { libc::getuid() as i64 },
"pgrp" => unsafe { libc::getpgrp() as i64 },
"pid" => std::process::id() as i64,
"ppid" => unsafe { libc::getppid() as i64 },
_ => return Err(mlua::Error::RuntimeError(format!("unknown getprocessid selector: {}", sel))),
};
Ok(mlua::Value::Integer(id))
} else {
let id_table = lua.create_table()?;
id_table.set("egid", unsafe { libc::getegid() as i64 })?;
id_table.set("euid", unsafe { libc::geteuid() as i64 })?;
id_table.set("gid", unsafe { libc::getgid() as i64 })?;
id_table.set("uid", unsafe { libc::getuid() as i64 })?;
id_table.set("pgrp", unsafe { libc::getpgrp() as i64 })?;
id_table.set("pid", std::process::id() as i64)?;
id_table.set("ppid", unsafe { libc::getppid() as i64 })?;
Ok(mlua::Value::Table(id_table))
}
})?)?;
posix_table.set("errno", lua.create_function(|lua, ()| -> LuaResult<mlua::MultiValue> {
let errno = unsafe { *errno_location() };
let err_msg = unsafe {
let c_str = libc::strerror(errno);
std::ffi::CStr::from_ptr(c_str).to_string_lossy().to_string()
};
let mut ret = mlua::MultiValue::new();
ret.push_front(mlua::Value::Integer(errno as i64));
ret.push_front(mlua::Value::String(lua.create_string(&err_msg)?));
Ok(ret)
})?)?;
posix_table.set("kill", lua.create_function(|lua, (pid, sig): (i32, Option<i32>)| -> LuaResult<MultiValue> {
let sig = sig.unwrap_or(libc::SIGTERM);
let result = unsafe { libc::kill(pid, sig) };
pushresult(lua, result, None)
})?)?;
posix_table.set("uname", lua.create_function(|_lua, format: Option<String>| -> LuaResult<String> {
let uname_info = posix_uname()
.map_err(|e| mlua::Error::RuntimeError(format!("uname: {:?}", e)))?;
let format_str = format.as_deref().unwrap_or("%s %n %r %v %m");
let mut result = String::new();
let mut chars = format_str.chars().peekable();
while let Some(ch) = chars.next() {
if ch != '%' {
result.push(ch);
} else {
match chars.next() {
Some('%') => result.push('%'),
Some('s') => result.push_str(&uname_info.sysname),
Some('n') => result.push_str(&uname_info.nodename),
Some('r') => result.push_str(&uname_info.release),
Some('v') => result.push_str(&uname_info.version),
Some('m') => result.push_str(&uname_info.machine),
Some(unknown) => {
return Err(mlua::Error::RuntimeError(
format!("unknown uname format option `{}'", unknown)
));
}
None => {
return Err(mlua::Error::RuntimeError(
"uname format string ends with incomplete % escape".to_string()
));
}
}
}
}
Ok(result)
})?)?;
posix_table.set("ctermid", lua.create_function(|lua, ()| -> LuaResult<mlua::Value> {
Ok(mlua::Value::String(lua.create_string(&posix_ctermid())?))
})?)?;
posix_table.set("dir", lua.create_function(|lua, path: Option<String>| -> LuaResult<mlua::Value> {
let path = path.as_deref().unwrap_or(".");
match posix_dir(path) {
Ok(entries) => {
let dir_table = lua.create_table()?;
for (i, entry) in entries.iter().enumerate() {
dir_table.set(i + 1, entry.as_str())?;
}
Ok(mlua::Value::Table(dir_table))
}
Err(_) => Ok(mlua::Value::Nil),
}
})?)?;
posix_table.set("files", lua.create_function(|lua, path: Option<String>| -> LuaResult<mlua::Value> {
let path = path.as_deref().unwrap_or(".");
let entries = match posix_dir(path) {
Ok(e) => e,
Err(_) => return Ok(mlua::Value::Nil),
};
let idx = std::cell::RefCell::new(0);
let entries_clone = entries.clone();
let iter_func = lua.create_function(move |lua, ()| {
let mut idx_val = idx.borrow_mut();
if *idx_val >= entries_clone.len() {
Ok(mlua::Value::Nil)
} else {
let name = &entries_clone[*idx_val];
*idx_val += 1;
Ok(mlua::Value::String(lua.create_string(name)?))
}
})?;
Ok(mlua::Value::Function(iter_func))
})?)?;
posix_table.set("times", lua.create_function(|lua, selector: Option<String>| -> LuaResult<mlua::Value> {
let times = posix_times()
.map_err(|e| mlua::Error::RuntimeError(format!("times: {:?}", e)))?;
if let Some(sel) = selector {
match sel.as_str() {
"utime" => Ok(mlua::Value::Number(times.utime)),
"stime" => Ok(mlua::Value::Number(times.stime)),
"cutime" => Ok(mlua::Value::Number(times.cutime)),
"cstime" => Ok(mlua::Value::Number(times.cstime)),
"elapsed" => Ok(mlua::Value::Number(times.elapsed)),
_ => Err(mlua::Error::RuntimeError(format!("unknown times selector: {}", sel))),
}
} else {
let times_table = lua.create_table()?;
times_table.set("utime", times.utime)?;
times_table.set("stime", times.stime)?;
times_table.set("cutime", times.cutime)?;
times_table.set("cstime", times.cstime)?;
times_table.set("elapsed", times.elapsed)?;
Ok(mlua::Value::Table(times_table))
}
})?)?;
posix_table.set("pathconf", lua.create_function(|lua, (path, selector): (String, Option<String>)| -> LuaResult<mlua::Value> {
let pathconf = posix_pathconf(&path)
.map_err(|e| mlua::Error::RuntimeError(format!("pathconf {}: {:?}", path, e)))?;
if let Some(sel) = selector {
match sel.as_str() {
"link_max" => Ok(mlua::Value::Integer(pathconf.link_max)),
"max_canon" => Ok(mlua::Value::Integer(pathconf.max_canon)),
"max_input" => Ok(mlua::Value::Integer(pathconf.max_input)),
"name_max" => Ok(mlua::Value::Integer(pathconf.name_max)),
"path_max" => Ok(mlua::Value::Integer(pathconf.path_max)),
"pipe_buf" => Ok(mlua::Value::Integer(pathconf.pipe_buf)),
"chown_restricted" => Ok(mlua::Value::Integer(pathconf.chown_restricted)),
"no_trunc" => Ok(mlua::Value::Integer(pathconf.no_trunc)),
"vdisable" => Ok(mlua::Value::Integer(pathconf.vdisable)),
_ => Err(mlua::Error::RuntimeError(format!("unknown pathconf selector: {}", sel))),
}
} else {
let pathconf_table = lua.create_table()?;
pathconf_table.set("link_max", pathconf.link_max)?;
pathconf_table.set("max_canon", pathconf.max_canon)?;
pathconf_table.set("max_input", pathconf.max_input)?;
pathconf_table.set("name_max", pathconf.name_max)?;
pathconf_table.set("path_max", pathconf.path_max)?;
pathconf_table.set("pipe_buf", pathconf.pipe_buf)?;
pathconf_table.set("chown_restricted", pathconf.chown_restricted)?;
pathconf_table.set("no_trunc", pathconf.no_trunc)?;
pathconf_table.set("vdisable", pathconf.vdisable)?;
Ok(mlua::Value::Table(pathconf_table))
}
})?)?;
posix_table.set("sysconf", lua.create_function(|lua, selector: Option<String>| -> LuaResult<mlua::Value> {
let sysconf = posix_sysconf()
.map_err(|e| mlua::Error::RuntimeError(format!("sysconf: {:?}", e)))?;
if let Some(sel) = selector {
match sel.as_str() {
"arg_max" => Ok(mlua::Value::Integer(sysconf.arg_max)),
"child_max" => Ok(mlua::Value::Integer(sysconf.child_max)),
"clk_tck" => Ok(mlua::Value::Integer(sysconf.clk_tck)),
"ngroups_max" => Ok(mlua::Value::Integer(sysconf.ngroups_max)),
"stream_max" => Ok(mlua::Value::Integer(sysconf.stream_max)),
"tzname_max" => Ok(mlua::Value::Integer(sysconf.tzname_max)),
"open_max" => Ok(mlua::Value::Integer(sysconf.open_max)),
"job_control" => Ok(mlua::Value::Integer(sysconf.job_control)),
"saved_ids" => Ok(mlua::Value::Integer(sysconf.saved_ids)),
"version" => Ok(mlua::Value::Integer(sysconf.version)),
_ => Err(mlua::Error::RuntimeError(format!("unknown sysconf selector: {}", sel))),
}
} else {
let sysconf_table = lua.create_table()?;
sysconf_table.set("arg_max", sysconf.arg_max)?;
sysconf_table.set("child_max", sysconf.child_max)?;
sysconf_table.set("clk_tck", sysconf.clk_tck)?;
sysconf_table.set("ngroups_max", sysconf.ngroups_max)?;
sysconf_table.set("stream_max", sysconf.stream_max)?;
sysconf_table.set("tzname_max", sysconf.tzname_max)?;
sysconf_table.set("open_max", sysconf.open_max)?;
sysconf_table.set("job_control", sysconf.job_control)?;
sysconf_table.set("saved_ids", sysconf.saved_ids)?;
sysconf_table.set("version", sysconf.version)?;
Ok(mlua::Value::Table(sysconf_table))
}
})?)?;
posix_table.set("fork", lua.create_function(|lua, ()| -> LuaResult<MultiValue> {
let result = unsafe { libc::fork() };
if result == 0 {
HAVE_FORKED.store(true, Ordering::Relaxed);
}
pushresult(lua, result, None)
})?)?;
posix_table.set("exec", lua.create_function(|lua, (path, args): (String, mlua::MultiValue)| -> LuaResult<MultiValue> {
use std::ffi::CString;
if !HAVE_FORKED.load(Ordering::Relaxed) {
return Err(mlua::Error::RuntimeError("exec not permitted in this context".to_string()));
}
let path_cstr = CString::new(path.clone())
.map_err(|_| mlua::Error::RuntimeError("exec: path contains null byte".to_string()))?;
let mut cstrings: Vec<CString> = vec![path_cstr];
for arg in args {
let arg_str = match arg {
mlua::Value::String(s) => s.to_str()?.to_string(),
_ => return Err(mlua::Error::RuntimeError("exec: all arguments must be strings".to_string())),
};
let arg_cstr = CString::new(arg_str)
.map_err(|_| mlua::Error::RuntimeError("exec: argument contains null byte".to_string()))?;
cstrings.push(arg_cstr);
}
let argv: Vec<*const libc::c_char> = cstrings.iter().map(|c| c.as_ptr() as *const libc::c_char).collect();
let mut argv_with_null = argv;
argv_with_null.push(std::ptr::null());
unsafe {
libc::execvp(cstrings[0].as_ptr(), argv_with_null.as_ptr());
return pusherror(lua, Some(&path));
}
})?)?;
posix_table.set("wait", lua.create_function(|lua, pid: Option<i32>| -> LuaResult<MultiValue> {
let pid = pid.unwrap_or(-1);
let mut status = 0;
let result = unsafe { libc::waitpid(pid, &mut status, 0) };
pushresult(lua, result, None)
})?)?;
posix_table.set("redirect2null", lua.create_function(|lua, target_fd: i32| -> LuaResult<MultiValue> {
use std::ffi::CString;
if !HAVE_FORKED.load(Ordering::Relaxed) {
return Err(mlua::Error::RuntimeError("redirect2null not permitted in this context".to_string()));
}
let null_path = CString::new("/dev/null")
.map_err(|_| mlua::Error::RuntimeError("/dev/null path error".to_string()))?;
let fd = unsafe { libc::open(null_path.as_ptr(), libc::O_WRONLY) };
let result = if fd >= 0 && fd != target_fd {
let saved_errno = unsafe { *errno_location() };
let r = unsafe { libc::dup2(fd, target_fd) };
unsafe { libc::close(fd) };
unsafe { *errno_location() = saved_errno };
r
} else {
fd
};
pushresult(lua, result, None)
})?)?;
Ok(())
}