From 39cbcd43fb544e60dbc50378613deec4d3d55bbf Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Mon, 9 Oct 2023 22:38:13 -0700 Subject: [PATCH] feat(*): add process label (resty-cli v0.30) (#12) --- .github/workflows/test-compat.yml | 12 ++++++ src/cli.rs | 33 ++++++++++++++-- src/nginx.rs | 4 +- src/util.rs | 62 ++++++++++++++++++++++++++++--- tests/lua-arg.sh | 5 ++- tests/lua/print-argv.lua | 57 ++++++++++++++++++++++++---- tests/runners.sh | 5 ++- tests/runners/bin/debug-argv | 2 +- 8 files changed, 159 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-compat.yml b/.github/workflows/test-compat.yml index b1dd963..4144eb3 100644 --- a/.github/workflows/test-compat.yml +++ b/.github/workflows/test-compat.yml @@ -56,6 +56,18 @@ jobs: --with-stream_ssl_module --with-stream_ssl_preread_module + - openresty: 1.21.4.2 + resty-cli: v0.30 + openssl: 1.1.1n + openresty-opts: > + --with-compat + --with-pcre-jit + --with-stream + --with-threads + --with-http_ssl_module + --with-stream_ssl_module + --with-stream_ssl_preread_module + steps: - name: install gdb run: | diff --git a/src/cli.rs b/src/cli.rs index 400c51d..d6fe14f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -162,6 +162,7 @@ pub struct NginxExec { prefix: String, runner: Runner, bin: String, + label: Option, } impl From for Command { @@ -180,6 +181,11 @@ impl From for Command { String::from("conf/nginx.conf"), ]; + if let Some(label) = ngx.label { + nginx_args.insert(0, String::from("-g")); + nginx_args.insert(1, label); + } + let bin: String; let mut args: Vec = vec![]; @@ -202,9 +208,7 @@ impl From for Command { } args.push("-c".to_owned()); nginx_args.insert(0, nginx); - args.push(join_shell_args( - nginx_args.iter_mut().map(|s| s.as_str()).collect(), - )); + args.push(join_shell_args(&nginx_args)); } Runner::Valgrind(opts) => { bin = "valgrind".to_owned(); @@ -485,6 +489,28 @@ impl Action { user.inline_lua.insert(0, jit.to_lua()); } + let mut label = None; + if get_resty_compat_version() >= 30 { + let mut s = String::from("# "); + if !user.inline_lua.is_empty() { + s.push_str("-e '"); + s.push_str(user.inline_lua.join("; ").as_ref()); + s.push('\''); + + if user.lua_file.is_some() { + s.push(' '); + } + } + + if let Some(fname) = &user.lua_file { + s.push_str(fname); + } + + s = s.replace(['\r', '\n'], ""); + + label = Some(s); + } + let lua_loader = match generate_lua_loader( &prefix, &user.lua_file, @@ -519,6 +545,7 @@ impl Action { bin: find_nginx_bin(user.nginx_bin).to_str().unwrap().to_string(), prefix: prefix.root.to_str().unwrap().to_string(), runner: user.runner, + label, }; run(Command::from(ngx)) diff --git a/src/nginx.rs b/src/nginx.rs index 787c89d..253065e 100644 --- a/src/nginx.rs +++ b/src/nginx.rs @@ -3,7 +3,7 @@ use std::env; use std::path::PathBuf; const RESTY_COMPAT_VAR: &str = "RESTY_CLI_COMPAT_VERSION"; -const RESTY_COMPAT_LATEST: u64 = 28; +const RESTY_COMPAT_LATEST: u64 = 30; const TEMPLATE: &str = include_str!("nginx.conf.tpl"); const TEMPLATE_NAME: &str = "nginx.conf"; @@ -69,7 +69,7 @@ pub fn find_nginx_bin(nginx: Option) -> PathBuf { PathBuf::from("nginx") } -fn get_resty_compat_version() -> u64 { +pub fn get_resty_compat_version() -> u64 { // TODO: maybe make this a build config item? match env::var_os(RESTY_COMPAT_VAR) { Some(value) => { diff --git a/src/util.rs b/src/util.rs index b2a4429..d29c673 100644 --- a/src/util.rs +++ b/src/util.rs @@ -38,12 +38,39 @@ pub(crate) fn try_parse_resolv_conf() -> Vec { nameservers } -pub fn split_shell_args(s: &str) -> Vec { - shlex::split(s).expect("Invalid runner options") +pub(crate) fn split_shell_args + ?Sized>(s: &T) -> Vec { + shlex::split(s.as_ref()).expect("Invalid runner options") } -pub fn join_shell_args(args: Vec<&str>) -> String { - shlex::join(args) +pub(crate) fn join_shell_args>(args: &Vec) -> String { + let mut out = Vec::with_capacity(args.len()); + + // The shlex crate takes a slightly different approach of wrapping the + // entire string in double quotes and then only escaping a few chars + // within the string. It's a little bit cleaner, but in the interest of + // compatibility we'll duplicate the resty-cli algorithm exactly: + // + // s/([\\\s'"><`\[\]\&\$#*?!()|;])/\\$1/g; + // + for arg in args { + let mut new = Vec::new(); + + for c in arg.as_ref().bytes() { + match c as char { + '\\' | ' ' | '\t' | '\r' | '\n' | '\'' | '"' | '`' | '<' | '>' | '[' | ']' + | '(' | ')' | '|' | ';' | '&' | '$' | '#' | '*' | '?' | '!' => { + new.push(b'\\'); + } + _ => {} + } + + new.push(c); + } + + out.push(String::from_utf8(new).unwrap()); + } + + out.join(" ") } #[cfg(test)] @@ -94,8 +121,8 @@ mod tests { #[test] fn test_join_shell_args() { assert_eq!( - "--nx -batch -ex \"b main\" -ex run -ex bt -ex \"b lj_cf_io_method_write\" -ex c -ex bt", - join_shell_args(vec![ + "--nx -batch -ex b\\ main -ex run -ex bt -ex b\\ lj_cf_io_method_write -ex c -ex bt", + join_shell_args(&vec![ "--nx", "-batch", "-ex", @@ -113,4 +140,27 @@ mod tests { ]) ); } + + #[test] + fn test_args_round_trip() { + let args = vec![ + "--nx", + "-batch", + "-ex", + "b main", + "--test", + "!", + "--test", + "($", + "'\\\\\\\"", + "`echo 123`", + ]; + + let joined = join_shell_args(&args); + let split = split_shell_args(&joined); + let rejoined = join_shell_args(&split); + let resplit = split_shell_args(&rejoined); + assert_eq!(joined, rejoined); + assert_eq!(args, resplit); + } } diff --git a/tests/lua-arg.sh b/tests/lua-arg.sh index b1e302a..1e713bd 100755 --- a/tests/lua-arg.sh +++ b/tests/lua-arg.sh @@ -93,7 +93,10 @@ run() { cmd+=( "${ARGS[@]}" ) - env - PATH="$RUNNER_PATH" "${cmd[@]}" \ + env - \ + PATH="$RUNNER_PATH" \ + RESTY_CLI_COMPAT_VERSION="${RESTY_CLI_COMPAT_VERSION:-0.28}" \ + "${cmd[@]}" \ > "$TMP/$name.stdout" \ 2> "$TMP/$name.stderr" diff --git a/tests/lua/print-argv.lua b/tests/lua/print-argv.lua index b4e79d5..d5c86b0 100644 --- a/tests/lua/print-argv.lua +++ b/tests/lua/print-argv.lua @@ -1,18 +1,61 @@ local fname = os.getenv("RUSTY_CLI_TEST_OUTPUT") or "/dev/stdout" local fh = assert(io.open(fname, "w+")) -local keys = {} +local PROC_SELF = "/proc/" .. ngx.worker.pid() -for k in pairs(arg) do - table.insert(keys, k) +local function exec(cmd) + local proc = io.popen(cmd, "r") + local out = proc:read("*a") + proc:close() + + out = out:gsub("\n$", "") + return out end -table.sort(keys) +local function get_cmd() + local proc = io.open(PROC_SELF .. "/cmdline", "r") + local data = proc:read("*a") + proc:close() + + local items = {} + + data:gsub("[^%z]+", function(item) + table.insert(items, item) + end) + + return items +end + +local function printf(...) + return fh:write(string.format(...)) +end + + +printf("CWD = %q\n", exec("readlink " .. PROC_SELF .. "/cwd")) +printf("EXE = %q\n", exec("readlink " .. PROC_SELF .. "/exe")) + +do + printf("CMD = {\n") + for i, elem in ipairs(get_cmd()) do + printf(" [%s] = %q\n", i, elem) + end + printf("}\n") +end + +do + local keys = {} + + for k in pairs(arg) do + table.insert(keys, k) + end -for _, k in ipairs(keys) do - local key = ("arg[%s]"):format(k) + table.sort(keys) - fh:write(("%-10s = %q\n"):format(key, arg[k])) + printf("ARG = {\n") + for _, k in ipairs(keys) do + printf(" [%s] = %q\n", k, arg[k]) + end + printf("}\n") end fh:close() diff --git a/tests/runners.sh b/tests/runners.sh index 50f57ff..8baa900 100755 --- a/tests/runners.sh +++ b/tests/runners.sh @@ -72,7 +72,10 @@ run() { cmd+=( "${ARGS[@]}" ) - env - PATH="$RUNNER_PATH" "${cmd[@]}" \ + env - \ + PATH="$RUNNER_PATH" \ + RESTY_CLI_COMPAT_VERSION="${RESTY_CLI_COMPAT_VERSION:-0.28}" \ + "${cmd[@]}" \ > "$TMP/$name.stdout" \ 2> "$TMP/$name.stderr" diff --git a/tests/runners/bin/debug-argv b/tests/runners/bin/debug-argv index 269e38e..2002924 100755 --- a/tests/runners/bin/debug-argv +++ b/tests/runners/bin/debug-argv @@ -4,5 +4,5 @@ printf 'ARGC = %d\n' "$#" printf 'ARGV[0] = %q\n' "$0" for ((i = 1; i <= $#; i++)); do - printf 'ARGV[%d] = %q\n' "$i" "${!i}" + printf 'ARGV[%d] = %s\n' "$i" "${!i}" done