Skip to content

Commit 667a2e9

Browse files
committed
Added a Rust wrapper for parsing cli args using GLib
1 parent 5a8d430 commit 667a2e9

8 files changed

Lines changed: 139 additions & 0 deletions

File tree

src/main/bindings/rust/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_custom_command(OUTPUT wrapper.rs
2525
--whitelist-function "descriptor_setHandle"
2626
--whitelist-function "process_.*CompatDescriptor"
2727
--whitelist-function "process_get.*Ptr"
28+
--whitelist-function "process_parseArgStr.*"
2829
--whitelist-function "shadow_logger_getDefault"
2930
--whitelist-function "shadow_logger_shouldFilter"
3031
--whitelist-function "statuslistener_ref"

src/main/bindings/rust/wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "main/core/logger/shadow_logger.h"
99
#include "main/host/descriptor/descriptor.h"
10+
#include "main/host/process.h"
1011
#include "main/host/status.h"
1112
#include "main/host/status_listener.h"
1213
#include "main/host/syscall_condition.h"

src/main/bindings/rust/wrapper.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,20 @@ extern "C" {
512512
n: size_t,
513513
) -> *mut ::std::os::raw::c_void;
514514
}
515+
extern "C" {
516+
pub fn process_parseArgStr(
517+
commandLine: *const ::std::os::raw::c_char,
518+
argc: *mut ::std::os::raw::c_int,
519+
argv: *mut *mut *mut ::std::os::raw::c_char,
520+
error: *mut *mut ::std::os::raw::c_char,
521+
) -> bool;
522+
}
523+
extern "C" {
524+
pub fn process_parseArgStrFree(
525+
argv: *mut *mut ::std::os::raw::c_char,
526+
error: *mut ::std::os::raw::c_char,
527+
);
528+
}
515529
extern "C" {
516530
pub fn descriptor_unref(data: gpointer);
517531
}

src/main/core/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
mod logger;
2+
pub mod support;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::ffi::{CStr, CString, OsStr, OsString};
2+
use std::os::unix::ffi::OsStrExt;
3+
4+
use crate::cshadow as c;
5+
6+
/// Parses a string as a list of arguments following the shell's parsing rules. This
7+
/// uses `g_shell_parse_argv()` for parsing.
8+
#[allow(dead_code)]
9+
fn parse_string_as_args(args_str: &OsStr) -> Result<Vec<OsString>, String> {
10+
if args_str.len() == 0 {
11+
return Ok(Vec::new());
12+
}
13+
14+
let args_str = CString::new(args_str.as_bytes()).unwrap();
15+
16+
// parse the argument string
17+
let mut argc: libc::c_int = 0;
18+
let mut argv: *mut *mut libc::c_char = std::ptr::null_mut();
19+
let mut error: *mut libc::c_char = std::ptr::null_mut();
20+
let rv = unsafe { c::process_parseArgStr(args_str.as_ptr(), &mut argc, &mut argv, &mut error) };
21+
22+
// if there was an error, return a copy of the error string
23+
if !rv {
24+
let error_message = match error.is_null() {
25+
false => unsafe { CStr::from_ptr(error) }.to_str().unwrap(),
26+
true => "Unknown parsing error",
27+
}
28+
.to_string();
29+
30+
unsafe { c::process_parseArgStrFree(argv, error) };
31+
return Err(error_message);
32+
}
33+
34+
assert!(!argv.is_null());
35+
36+
// copy the arg strings
37+
let args: Vec<_> = (0..argc)
38+
.map(|x| unsafe {
39+
let arg_ptr = *argv.add(x as usize);
40+
assert!(!arg_ptr.is_null());
41+
OsStr::from_bytes(CStr::from_ptr(arg_ptr).to_bytes()).to_os_string()
42+
})
43+
.collect();
44+
45+
unsafe { c::process_parseArgStrFree(argv, error) };
46+
Ok(args)
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use super::*;
52+
53+
#[test]
54+
fn test_parse_args() {
55+
let arg_str = r#"the quick brown fox "jumped over" the "\"lazy\" dog""#;
56+
let expected_args = &[
57+
"the",
58+
"quick",
59+
"brown",
60+
"fox",
61+
"jumped over",
62+
"the",
63+
"\"lazy\" dog",
64+
];
65+
66+
let arg_str: OsString = arg_str.into();
67+
let args = parse_string_as_args(&arg_str).unwrap();
68+
69+
assert_eq!(args, expected_args);
70+
}
71+
72+
#[test]
73+
fn test_parse_args_empty() {
74+
let arg_str = "";
75+
let expected_args: &[&str] = &[];
76+
77+
let arg_str: OsString = arg_str.into();
78+
let args = parse_string_as_args(&arg_str).unwrap();
79+
80+
assert_eq!(args, expected_args);
81+
}
82+
83+
#[test]
84+
fn test_parse_args_error() {
85+
let arg_str = r#"hello "world"#;
86+
87+
let arg_str: OsString = arg_str.into();
88+
let err_str = parse_string_as_args(&arg_str).unwrap_err();
89+
90+
assert!(err_str.len() != 0);
91+
}
92+
}

src/main/core/support/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod configuration;

src/main/host/process.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,3 +967,26 @@ LegacyDescriptor* process_getRegisteredLegacyDescriptor(Process* proc, int handl
967967

968968
return legacyDesc;
969969
}
970+
971+
bool process_parseArgStr(const char* commandLine, int* argc, char*** argv, char** error) {
972+
GError* gError = NULL;
973+
974+
bool rv = !!g_shell_parse_argv(commandLine, argc, argv, &gError);
975+
if (!rv && gError != NULL && gError->message != NULL && error != NULL) {
976+
*error = strdup(gError->message);
977+
}
978+
979+
if (gError != NULL) {
980+
g_error_free(gError);
981+
}
982+
return rv;
983+
}
984+
985+
void process_parseArgStrFree(char** argv, char* error) {
986+
if (argv != NULL) {
987+
g_strfreev(argv);
988+
}
989+
if (error != NULL) {
990+
g_free(error);
991+
}
992+
}

src/main/host/process.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,10 @@ void process_setMemoryManager(Process* proc, MemoryManager* memoryManager);
141141
// Returns the interpose method used by this process.
142142
InterposeMethod process_getInterposeMethod(Process* proc);
143143

144+
// A wrapper around GLib's `g_shell_parse_argv()` that doesn't use GLib types. The returned
145+
// pointers must be freed using `process_parseArgStrFree()`.
146+
bool process_parseArgStr(const char* commandLine, int* argc, char*** argv, char** error);
147+
// Free all data allocated by `process_parseArgStr()`.
148+
void process_parseArgStrFree(char** argv, char* error);
149+
144150
#endif /* SHD_PROCESS_H_ */

0 commit comments

Comments
 (0)