Skip to content
Snippets Groups Projects
Commit 9223e526 authored by Yaroslav Dynnikov's avatar Yaroslav Dynnikov
Browse files

Add picodata entrypoint

This patch introduces the new cargo package - picodata.

The package consists of two parts:
- The `picodata` binary does some env magic and executes
  `tarantool -l picodata`.
- The `libpicodata` dylib is the starting point for further
  development.
parent 01e54693
No related branches found
No related tags found
No related merge requests found
[package]
name = "demo"
name = "picodata"
version = "0.1.0"
edition = "2021"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2.108"
errno = "0.2.8"
[dev-dependencies]
assert_cmd = "2.0"
tempfile = "3"
[lib]
test = false
crate-type = ["cdylib"]
[[bin]]
name = "picodata"
test = false
use libc::c_char;
use std::path;
use std::ffi::CString;
#[must_use]
pub fn execve(argv: Vec<CString>, envp: Vec<CString>) -> errno::Errno {
let mut argv_p: Vec<*const c_char> = Vec::new();
for s in argv.iter() {
argv_p.push(s.as_ptr());
}
argv_p.push(std::ptr::null());
let mut envp_p: Vec<*const c_char> = Vec::new();
for s in envp.iter() {
envp_p.push(s.as_ptr());
}
envp_p.push(std::ptr::null());
unsafe {
libc::execve(argv[0].as_ptr(), argv_p.as_ptr(), envp_p.as_ptr());
};
// execve doesn't retun unless it fails
errno::errno()
}
pub fn which(prog: &str) -> Option<path::PathBuf> {
for p in std::env::var("PATH").unwrap().split(":") {
let pb: path::PathBuf = [p, prog].iter().collect();
if pb.as_path().exists() {
return Some(pb);
}
}
None
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
use std::os::raw::c_int;
#[no_mangle]
pub extern "C" fn luaopen_picodata(_l: std::ffi::c_void) -> c_int {
println!("Hello from rust lib");
0
}
use std::collections::HashMap;
use std::ffi::CString;
mod execve;
macro_rules! CString {
($s:expr) => {
CString::new($s).expect("CString::new failed")
};
}
fn main() {
let tarantool_path: String = match execve::which("tarantool") {
Some(v) => v
.into_os_string()
.into_string()
.expect("OsString::into_string failed"),
None => {
eprintln!("tarantool: {}", errno::Errno(libc::ENOENT));
std::process::exit(-1);
}
};
let args = [&tarantool_path, "-l", "picodata"];
let argv = std::iter::empty()
.chain(args.iter().map(|&s| CString!(s)))
.chain(std::env::args().skip(1).map(|s| CString!(s)))
.collect();
let mut envp = HashMap::new();
for (k, v) in std::env::vars() {
if !k.starts_with("TT_") && !k.starts_with("TARANTOOL_") {
envp.insert(k, v);
}
}
// In order to make libpicodata loaded we should provide
// its path in the LUA_CPATH env variable.
let cpath: String = {
// The lib is located in the same dir as the executable
let mut p = std::env::current_exe().unwrap();
p.pop();
#[cfg(target_os = "macos")]
p.push("lib?.dylib");
#[cfg(target_os = "linux")]
p.push("lib?.so");
p.into_os_string().into_string().unwrap()
};
envp.entry("LUA_CPATH".to_owned())
.and_modify(|v| *v = format!("{};{}", cpath, v))
.or_insert(cpath);
let envp = envp
.into_iter()
.map(|(k, v)| CString!(format!("{}={}", k, v)))
.collect();
println!("Hello from picodata main");
let e = execve::execve(argv, envp);
eprintln!("{}: {}", tarantool_path, e);
std::process::exit(-1);
}
use assert_cmd::Command;
use std::fs::File;
use tempfile::tempdir;
#[test]
fn positive() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.assert().success();
}
#[test]
fn missing_tarantool() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.env("PATH", "/nowhere");
cmd.assert()
.failure()
.stderr("tarantool: No such file or directory\n");
}
#[test]
fn broken_tarantool() {
let temp = tempdir().unwrap();
let temp_path = temp.path();
File::create(temp_path.join("tarantool")).unwrap();
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.env("PATH", temp_path);
cmd.assert().failure().stderr(format!(
"{}/tarantool: {}\n",
temp_path.display(),
errno::Errno(libc::EACCES)
));
}
#[test]
fn pass_arguments() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.args(["-e", "error('xxx', 0)"]);
cmd.assert().failure().stderr(
"LuajitError: xxx\n\
fatal error, exiting the event loop\n",
);
}
#[test]
fn pass_environment() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.env("LUA_CPATH", "/dev/null/?");
cmd.env("CUSTOM_VAR", "keep me");
cmd.env("TARANTOOL_VAR", "purge me");
cmd.env("TT_VAR", "purge me too");
cmd.arg("-e").arg(
r#"
cpath = os.environ()['LUA_CPATH']
assert(cpath and cpath:endswith(';/dev/null/?'), cpath)
function assert_eq(l, r)
if l ~= r then
error(('Assertion failed: %q ~= %q'):format(l, r), 2)
end
end
assert_eq(os.environ()['CUSTOM_VAR'], 'keep me')
assert_eq(os.environ()['TARANTOOL_VAR'], nil)
assert_eq(os.environ()['TT_VAR'], nil)
"#,
);
cmd.assert().success();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment