From 3f79ddf06375c3b34569c8b9ce91a607bbd0f052 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sun, 28 Jun 2020 15:14:04 +0200 Subject: pkgs/profpatsch/execline: add el_exec and el_substitute el_exec: wraps the various execve wrappers in skalib that are useful for writing execline-like utils. currently only `xpathexec0` is supported, which execs into the argv you give it or errors with the right error if file not found. el_substitute: execline argv substitution! Wraps the execline function, so it will behave exactly the same as the existing execline utils, like `importas`. --- pkgs/profpatsch/default.nix | 6 +- pkgs/profpatsch/execline/default.nix | 34 ++++++ pkgs/profpatsch/execline/el-semicolon.nix | 19 --- pkgs/profpatsch/execline/el_exec.rs | 32 +++++ pkgs/profpatsch/execline/el_substitute.rs | 190 ++++++++++++++++++++++++++++++ pkgs/profpatsch/rust-deps.nix | 24 ++++ 6 files changed, 285 insertions(+), 20 deletions(-) create mode 100644 pkgs/profpatsch/execline/default.nix create mode 100644 pkgs/profpatsch/execline/el_exec.rs create mode 100644 pkgs/profpatsch/execline/el_substitute.rs create mode 100644 pkgs/profpatsch/rust-deps.nix (limited to 'pkgs/profpatsch') diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index 993189ff..f39831b2 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -210,7 +210,9 @@ in rec { readDhallFileAsJson ; + rust-deps = (import ./rust-deps.nix { inherit (pkgs) buildRustCrate; }); inherit (import ./xdg-open { inherit pkgs getBins importDhall2 writeExecline dhall buildDhallPackage; }) + xdg-open Prelude ; @@ -220,9 +222,11 @@ in rec { netencode-rs-tests ; - inherit (import ./execline/el-semicolon.nix { inherit writeRustSimpleLib; }) + inherit (import ./execline/default.nix { inherit pkgs writeRustSimpleLib rust-deps; }) el-semicolon el-semicolon-tests + el-exec + el-substitute ; netencode = import ./netencode/netencode.nix; diff --git a/pkgs/profpatsch/execline/default.nix b/pkgs/profpatsch/execline/default.nix new file mode 100644 index 00000000..317ca97d --- /dev/null +++ b/pkgs/profpatsch/execline/default.nix @@ -0,0 +1,34 @@ +{ pkgs, writeRustSimpleLib, rust-deps }: + +let + el-semicolon-common = tests: writeRustSimpleLib "el_semicolon" { + buildTests = tests; + release = false; + verbose = true; + } ./el_semicolon.rs; + + el-semicolon-tests = el-semicolon-common true; + el-semicolon = el-semicolon-common false; + + el-exec = writeRustSimpleLib "el_exec" { + dependencies = [ rust-deps.libc ]; + buildInputs = [ pkgs.skalibs ]; + release = false; + verbose = true; + } ./el_exec.rs; + + el-substitute = writeRustSimpleLib "el_substitute" { + dependencies = [ rust-deps.libc rust-deps.errno ]; + buildInputs = [ pkgs.skalibs ]; + release = false; + verbose = true; + } ./el_substitute.rs; + +in { + inherit + el-semicolon + el-semicolon-tests + el-exec + el-substitute + ; +} diff --git a/pkgs/profpatsch/execline/el-semicolon.nix b/pkgs/profpatsch/execline/el-semicolon.nix index 73694bdb..e69de29b 100644 --- a/pkgs/profpatsch/execline/el-semicolon.nix +++ b/pkgs/profpatsch/execline/el-semicolon.nix @@ -1,19 +0,0 @@ -{ writeRustSimpleLib }: - -let - el-semicolon-common = tests: writeRustSimpleLib "el_semicolon" { - buildTests = tests; - release = false; - verbose = true; - } ./el_semicolon.rs; - - el-semicolon-tests = el-semicolon-common true; - - el-semicolon = el-semicolon-common false; - -in { - inherit - el-semicolon - el-semicolon-tests - ; -} diff --git a/pkgs/profpatsch/execline/el_exec.rs b/pkgs/profpatsch/execline/el_exec.rs new file mode 100644 index 00000000..25fad36f --- /dev/null +++ b/pkgs/profpatsch/execline/el_exec.rs @@ -0,0 +1,32 @@ +use std::ffi::CStr; +extern crate libc; + +fn to_c_argv>(s: &[S]) -> (&[S], Vec<*const libc::c_char>) { + let mut c_ptr_args = s.iter() + .map(|s| s.as_ref().as_ptr()) + .collect::>(); + c_ptr_args.push(std::ptr::null()); + (s, c_ptr_args) +} + +/// Exec into argv, or exit 0 if it’s empty. +/// Will throw 127 if the executable is not found (ENOENT) +/// and 126 for any other exec error. +pub fn xpathexec0<'a, S: AsRef>(argv: &'a [S]) { + let (c_strings, c_argv) = to_c_argv(argv); + + unsafe { + C::xpathexec0( + c_argv.as_ptr() as *const *const libc::c_char + ) + } +} + +mod C { + #[link(name = "skarnet")] + extern "C" { + pub fn xpathexec0( + argv: *const *const libc::c_char + ); + } +} diff --git a/pkgs/profpatsch/execline/el_substitute.rs b/pkgs/profpatsch/execline/el_substitute.rs new file mode 100644 index 00000000..b7e98cde --- /dev/null +++ b/pkgs/profpatsch/execline/el_substitute.rs @@ -0,0 +1,190 @@ +extern crate errno; +extern crate libc; + +use std::ffi::{CStr, CString}; + +#[repr(C)] +pub struct Stralloc_C { + s: *mut libc::c_char, + len: libc::size_t, + a: libc::size_t +} + +fn stralloc_zero() -> Stralloc_C { + Stralloc_C { + s: std::ptr::null_mut(), + len: 0, + a: 0 + } +} + +#[link(name = "skarnet")] +extern "C" { + fn stralloc_free(sa: *mut Stralloc_C); + fn stralloc_ready_tuned( + sa: *mut Stralloc_C, + n: libc::size_t, + base: libc::size_t, + a: libc::size_t, + b: libc::size_t, + ) -> libc::c_int; + + fn stralloc_copyb( + sa: *mut Stralloc_C, + s: *const libc::c_char, + len: libc::size_t + ) -> libc::c_int; +} + +fn stralloc_ready(sa: *mut Stralloc_C, n: libc::size_t) { + unsafe { + if (stralloc_ready_tuned(sa, n, 8, 1, 8) == 0) { + panic!("{}", errno::errno()); + } + } +} + +struct Stralloc(Stralloc_C); + +impl Stralloc { + fn new() -> Self { + let mut sa = stralloc_zero(); + unsafe { + stralloc_ready(&mut sa, 0); + } + Stralloc(sa) + } +} + +impl AsMut for Stralloc { + fn as_mut(&mut self) -> &mut Stralloc_C { + match self { + Stralloc(s) => s + } + } +} + +impl AsRef for Stralloc { + fn as_ref(&self) -> &Stralloc_C { + match self { + Stralloc(s) => s + } + } +} + +impl<'a> From<&mut [u8]> for Stralloc { + fn from(s: &mut [u8]) -> Self { + let mut sa = stralloc_zero(); + let ptr = s.as_mut_ptr() as *mut libc::c_char; + unsafe { + if stralloc_copyb(&mut sa, ptr, s.len()) == 0 { + panic!("{}", errno::errno()); + } + } + Stralloc(sa) + } +} + +// TODO not sure if stralloc will always be a contiguous block? +// that’s the precondition for from_raw_parts +impl AsRef<[u8]> for Stralloc_C { + fn as_ref(&self) -> &[u8] { + let ptr = self.s as *const u8; + unsafe { + std::slice::from_raw_parts(ptr, self.len) + } + } +} + +impl Drop for Stralloc { + fn drop(&mut self) { + match self { + Stralloc(inner) => { + unsafe { + stralloc_free(inner); + } + } + } + } +} + + +#[repr(C)] +#[derive(Debug)] +pub struct Elsubst_C +{ + var: libc::size_t, + value: libc::size_t, + // values are \0-separated strings, + // and n is the amount of elements in one such string + n: libc::c_uint +} + +#[link(name = "execline")] +extern "C" { + fn el_substitute( + dst: *mut Stralloc_C, + src: *const libc::c_char, + len: libc::size_t, + // length is given by nsubst + vars: *const libc::c_char, + // length is given by nsubst + values: *const libc::c_char, + + substs: *const Elsubst_C, + nsubst: libc::size_t + ) -> libc::c_int; +} + +pub struct Subst<'a> { + pub var: &'a CStr, + pub value: &'a CStr, +} + +fn simple_substitute<'a, 'b>(subst: &[Subst<'a>], src: &CStr) -> CString { + let len = src.to_bytes_with_nul().len() as libc::size_t; + let src = src.as_ptr() as *const libc::c_char; + let mut vars : Vec = Vec::new(); + let mut values : Vec = Vec::new(); + let mut substs : Vec = Vec::new(); + let mut var_i = 0; + let mut value_i = 0; + for s in subst { + let var = s.var.to_bytes_with_nul(); + let value = s.value.to_bytes_with_nul(); + vars.extend_from_slice(var); + values.extend_from_slice(value); + substs.push(Elsubst_C { + // these index into the vars/values arrays given to el_substitute + var: var_i, + value: value_i, + // we don’t deal with split values here + n: 1 + }); + var_i += var.len(); + value_i += value.len(); + } + let nsubst = subst.len(); + let mut dst = stralloc_zero(); + unsafe { + if el_substitute( + &mut dst, + src, + len, + vars.as_ptr() as *const libc::c_char, + values.as_ptr() as *const libc::c_char, + substs.as_ptr() as *const Elsubst_C, + nsubst + ) == -1 { + panic!("{}", errno::errno()); + } + // el_substitute returns a \0-delim C string + CStr::from_bytes_with_nul_unchecked(dst.as_ref()).to_owned() + } +} + +pub fn simple_substitute_argv<'a, 'b, S: AsRef>(subst: &[Subst<'a>], argv: &'b [S]) -> Vec { + argv.into_iter() + .map(|arg| simple_substitute(subst, arg.as_ref())) + .collect::>() +} diff --git a/pkgs/profpatsch/rust-deps.nix b/pkgs/profpatsch/rust-deps.nix new file mode 100644 index 00000000..d6a52258 --- /dev/null +++ b/pkgs/profpatsch/rust-deps.nix @@ -0,0 +1,24 @@ +{ buildRustCrate }: + +let + libc = buildRustCrate { + pname = "libc"; + version = "0.2.69"; + crateName = "libc"; + sha256 = "0fwi6rxklsaqcig432fg3cjamiilvv2c4jz0i3dxw7c33ipprhsz"; + }; + + errno = buildRustCrate { + pname = "errno"; + version = "0.2.5"; + crateName = "errno"; + sha256 = "0gd36jijlb17df3ffxqxqczlwdawicbbzqjwfjc4b5lzqgizm0bz"; + dependencies = [ libc ]; + }; + +in { + inherit + libc + errno + ; +} -- cgit 1.4.1