#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::ported::utils::zwarnnam;
use crate::ported::zsh_h::{module, options};
use std::ffi::{CStr, CString};
#[cfg(all(target_os = "linux", feature = "libcap"))]
mod ffi {
use libc::{c_char, c_int, c_void, ssize_t};
pub type CapT = *mut c_void;
#[link(name = "cap")]
extern "C" {
pub fn cap_get_proc() -> CapT;
pub fn cap_set_proc(cap_p: CapT) -> c_int;
pub fn cap_get_file(path: *const c_char) -> CapT;
pub fn cap_set_file(path: *const c_char, cap_p: CapT) -> c_int;
pub fn cap_from_text(buf: *const c_char) -> CapT;
pub fn cap_to_text(caps: CapT, length: *mut ssize_t) -> *mut c_char;
pub fn cap_free(obj: *mut c_void) -> c_int;
}
}
#[cfg(all(target_os = "linux", feature = "libcap"))]
pub(crate) fn bin_cap(nam: &str, argv: &[String], _ops: &options, _func: i32) -> i32 {
let mut ret = 0;
if let Some(arg0) = argv.first() {
let arg_c = match CString::new(arg0.as_str()) {
Ok(c) => c,
Err(_) => {
zwarnnam(nam, "invalid capability string");
return 1;
}
};
unsafe {
let caps = ffi::cap_from_text(arg_c.as_ptr());
if caps.is_null() {
zwarnnam(nam, "invalid capability string");
return 1;
}
if ffi::cap_set_proc(caps) != 0 {
zwarnnam(
nam,
&format!("can't change capabilities: {}", std::io::Error::last_os_error()),
);
ret = 1;
}
ffi::cap_free(caps);
}
} else {
unsafe {
let caps = ffi::cap_get_proc();
let result = if !caps.is_null() {
ffi::cap_to_text(caps, std::ptr::null_mut())
} else {
std::ptr::null_mut()
};
if caps.is_null() || result.is_null() {
zwarnnam(
nam,
&format!("can't get capabilities: {}", std::io::Error::last_os_error()),
);
ret = 1;
} else {
let s = CStr::from_ptr(result).to_string_lossy();
println!("{}", s);
}
if !result.is_null() {
ffi::cap_free(result as *mut libc::c_void);
}
if !caps.is_null() {
ffi::cap_free(caps);
}
}
}
ret
}
#[cfg(not(all(target_os = "linux", feature = "libcap")))]
pub(crate) fn bin_cap(nam: &str, _argv: &[String], _ops: &options, _func: i32) -> i32 {
zwarnnam(nam, "not available on this host");
1
}
#[cfg(all(target_os = "linux", feature = "libcap"))]
pub(crate) fn bin_getcap(nam: &str, argv: &[String], _ops: &options, _func: i32) -> i32 {
let mut ret = 0;
for file in argv {
let path_c = match CString::new(file.as_str()) {
Ok(c) => c,
Err(_) => {
zwarnnam(nam, &format!("{}: invalid path", file));
ret = 1;
continue;
}
};
unsafe {
let caps = ffi::cap_get_file(path_c.as_ptr());
let result = if !caps.is_null() {
ffi::cap_to_text(caps, std::ptr::null_mut())
} else {
std::ptr::null_mut()
};
if caps.is_null() || result.is_null() {
zwarnnam(nam, &format!("{}: {}", file, std::io::Error::last_os_error()));
ret = 1;
} else {
let s = CStr::from_ptr(result).to_string_lossy();
println!("{} {}", file, s);
}
if !result.is_null() {
ffi::cap_free(result as *mut libc::c_void);
}
if !caps.is_null() {
ffi::cap_free(caps);
}
}
}
ret
}
#[cfg(not(all(target_os = "linux", feature = "libcap")))]
pub(crate) fn bin_getcap(nam: &str, _argv: &[String], _ops: &options, _func: i32) -> i32 {
zwarnnam(nam, "not available on this host");
1
}
#[cfg(all(target_os = "linux", feature = "libcap"))]
pub(crate) fn bin_setcap(nam: &str, argv: &[String], _ops: &options, _func: i32) -> i32 {
let mut ret = 0;
let cap_str = match argv.first() {
Some(s) => s.as_str(),
None => {
zwarnnam(nam, "invalid capability string");
return 1;
}
};
let cap_c = match CString::new(cap_str) {
Ok(c) => c,
Err(_) => {
zwarnnam(nam, "invalid capability string");
return 1;
}
};
unsafe {
let caps = ffi::cap_from_text(cap_c.as_ptr());
if caps.is_null() {
zwarnnam(nam, "invalid capability string");
return 1;
}
for file in &argv[1..] {
let path_c = match CString::new(file.as_str()) {
Ok(c) => c,
Err(_) => {
zwarnnam(nam, &format!("{}: invalid path", file));
ret = 1;
continue;
}
};
if ffi::cap_set_file(path_c.as_ptr(), caps) != 0 {
zwarnnam(nam, &format!("{}: {}", file, std::io::Error::last_os_error()));
ret = 1;
}
}
ffi::cap_free(caps);
}
ret
}
#[cfg(not(all(target_os = "linux", feature = "libcap")))]
pub(crate) fn bin_setcap(nam: &str, _argv: &[String], _ops: &options, _func: i32) -> i32 {
zwarnnam(nam, "not available on this host");
1
}
#[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 }
#[cfg(test)]
mod tests {
use super::*;
fn empty_ops() -> options {
options { ind: [0u8; crate::ported::zsh_h::MAX_OPS], args: Vec::new(), argscount: 0, argsalloc: 0 }
}
#[test]
fn test_features_returns_bintab_names() {
let m: *const module = std::ptr::null();
let mut features: Vec<String> = Vec::new();
let rc = features_(m, &mut features);
assert_eq!(rc, 0);
assert_eq!(features, vec!["b:cap", "b:getcap", "b:setcap"]);
}
#[test]
fn test_enables_get_then_set() {
let m: *const module = std::ptr::null();
let mut enables: Option<Vec<i32>> = None;
let rc = enables_(m, &mut enables);
assert_eq!(rc, 0);
let v = enables.as_ref().unwrap();
assert_eq!(v.len(), 3);
let rc = enables_(m, &mut enables);
assert_eq!(rc, 0);
}
#[test]
fn test_cleanup_returns_zero() {
let m: *const module = std::ptr::null();
assert_eq!(cleanup_(m), 0);
}
#[test]
#[cfg(not(all(target_os = "linux", feature = "libcap")))]
fn test_bin_cap_unsupported_on_macos() {
let ops = empty_ops();
assert_eq!(bin_cap("cap", &[], &ops, 0), 1);
assert_eq!(bin_getcap("getcap", &["/etc/passwd".into()], &ops, 0), 1);
assert_eq!(
bin_setcap("setcap", &["cap_net_admin+ep".into(), "/tmp/x".into()], &ops, 0),
1
);
}
}
use crate::ported::zsh_h::features as features_t;
use std::sync::{Mutex, OnceLock};
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 3,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:cap".to_string(), "b:getcap".to_string(), "b:setcap".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 3]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}