Compare commits

...

1 Commits

Author SHA1 Message Date
Owen Lin
4784ac20bf chore(zsh): add patched-zsh patch artifact and build script 2026-02-17 15:57:33 -08:00
2 changed files with 325 additions and 5 deletions

126
scripts/build-patched-zsh.sh Executable file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail
# Build the Codex-patched zsh used by shell-tool-mcp.
#
# Defaults are intentionally override-friendly:
# CODEX_REPO Codex repo root containing shell-tool-mcp/patches
# ZSH_SRC_DIR Working clone location for zsh source
# INSTALL_PREFIX Install prefix for make install
# JOBS Parallel jobs for make
# ZSH_REMOTE Upstream zsh remote
# INSTALL_DOCS Set to 1 to run full `make install` (includes manpages)
ZSH_COMMIT="77045ef899e53b9598bebc5a41db93a548a40ca6"
ZSH_REMOTE="${ZSH_REMOTE:-https://git.code.sf.net/p/zsh/code}"
INSTALL_PREFIX="${INSTALL_PREFIX:-$HOME/.local/codex-zsh-${ZSH_COMMIT:0:7}}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
resolve_codex_repo() {
if [[ -n "${CODEX_REPO:-}" ]]; then
printf '%s\n' "$CODEX_REPO"
return
fi
# Most reliable when this script is run from inside the codex repo.
local from_script
from_script="$(cd -- "$SCRIPT_DIR/.." && pwd)"
if [[ -f "$from_script/shell-tool-mcp/patches/zsh-exec-wrapper.patch" ]]; then
printf '%s\n' "$from_script"
return
fi
# Common local workspace layouts.
if [[ -f "$HOME/repos/codex/shell-tool-mcp/patches/zsh-exec-wrapper.patch" ]]; then
printf '%s\n' "$HOME/repos/codex"
return
fi
if [[ -f "$HOME/code/codex/shell-tool-mcp/patches/zsh-exec-wrapper.patch" ]]; then
printf '%s\n' "$HOME/code/codex"
return
fi
echo "Could not locate codex repo. Set CODEX_REPO=/path/to/codex." >&2
exit 1
}
resolve_zsh_src_dir() {
if [[ -n "${ZSH_SRC_DIR:-}" ]]; then
printf '%s\n' "$ZSH_SRC_DIR"
return
fi
if [[ -d "$HOME/repos/zsh/.git" ]]; then
printf '%s\n' "$HOME/repos/zsh"
return
fi
if [[ -d "$HOME/code/zsh/.git" ]]; then
printf '%s\n' "$HOME/code/zsh"
return
fi
# Fallback for users without an existing clone.
printf '%s\n' "$HOME/src/zsh-code"
}
CODEX_REPO="$(resolve_codex_repo)"
ZSH_SRC_DIR="$(resolve_zsh_src_dir)"
PATCH_FILE="$CODEX_REPO/shell-tool-mcp/patches/zsh-exec-wrapper.patch"
if [[ ! -f "$PATCH_FILE" ]]; then
echo "Patch file not found: $PATCH_FILE" >&2
exit 1
fi
if [[ -z "${JOBS:-}" ]]; then
if command -v nproc >/dev/null 2>&1; then
JOBS="$(nproc)"
elif command -v sysctl >/dev/null 2>&1; then
JOBS="$(sysctl -n hw.ncpu)"
else
JOBS=4
fi
fi
mkdir -p "$(dirname -- "$ZSH_SRC_DIR")"
if [[ ! -d "$ZSH_SRC_DIR/.git" ]]; then
git clone "$ZSH_REMOTE" "$ZSH_SRC_DIR"
fi
git -C "$ZSH_SRC_DIR" fetch --depth 1 origin "$ZSH_COMMIT"
git -C "$ZSH_SRC_DIR" checkout --detach -f "$ZSH_COMMIT"
git -C "$ZSH_SRC_DIR" reset --hard "$ZSH_COMMIT"
if git -C "$ZSH_SRC_DIR" apply --reverse --check "$PATCH_FILE" >/dev/null 2>&1; then
echo "Patch already applied: $PATCH_FILE"
else
git -C "$ZSH_SRC_DIR" apply --check "$PATCH_FILE"
git -C "$ZSH_SRC_DIR" apply "$PATCH_FILE"
fi
(
cd "$ZSH_SRC_DIR"
./Util/preconfig
./configure --prefix="$INSTALL_PREFIX"
make -j"$JOBS"
if [[ "${INSTALL_DOCS:-0}" == "1" ]]; then
make install
else
make install.bin
fi
)
cat <<OUT
Built patched zsh successfully.
Binary:
$INSTALL_PREFIX/bin/zsh
Quick checks:
$INSTALL_PREFIX/bin/zsh --version
$INSTALL_PREFIX/bin/zsh -fc '/bin/echo smoke-zsh'
OUT

View File

@@ -1,34 +1,228 @@
diff --git a/Src/exec.c b/Src/exec.c
index 27bca11..baea760 100644
index 27bca110c..4e5000be3 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -507,7 +507,9 @@ zexecve(char *pth, char **argv, char **newenvp)
@@ -507,7 +507,12 @@ zexecve(char *pth, char **argv, char **newenvp)
{
int eno;
static char buf[PATH_MAX * 2+1];
- char **eep;
+ char **eep, **exec_argv;
+ char **eep, **exec_argv, **wrapper_envp;
+ char *orig_pth = pth;
+ char *exec_wrapper;
+ char wrapper_origin_buf[64];
+ int wrapper_origin_len;
+ int wrapper_envc, wrapper_origin_idx;
unmetafy(pth, NULL);
for (eep = argv; *eep; eep++)
@@ -526,8 +528,17 @@ zexecve(char *pth, char **argv, char **newenvp)
@@ -526,8 +531,51 @@ zexecve(char *pth, char **argv, char **newenvp)
if (newenvp == NULL)
newenvp = environ;
+ exec_argv = argv;
+ /* Use the command env by default; wrapper mode may replace this with an
+ * explicitly rebuilt envp so origin metadata is guaranteed to be present
+ * even when execve does not use the process-global environ. */
+ wrapper_envp = newenvp;
+ if ((exec_wrapper = getenv("EXEC_WRAPPER")) &&
+ *exec_wrapper && !inblank(*exec_wrapper)) {
+ /* zexecve callers provide spare argv slots before argv[0] for
+ * interpreter dispatch; reuse those slots to trampoline through the
+ * wrapper binary while preserving the original target path. */
+ exec_argv = argv - 2;
+ exec_argv[0] = exec_wrapper;
+ exec_argv[1] = orig_pth;
+ wrapper_origin_len = sprintf(wrapper_origin_buf,
+ "CODEX_ZSH_EXEC_BRIDGE_WRAPPER_ORIGIN=%d",
+ zsh_exec_wrapper_origin);
+ if (wrapper_origin_len > 0 &&
+ wrapper_origin_len < (int)sizeof(wrapper_origin_buf)) {
+ wrapper_envc = 0;
+ wrapper_origin_idx = -1;
+ for (eep = newenvp; *eep; eep++) {
+ if (strncmp(*eep, "CODEX_ZSH_EXEC_BRIDGE_WRAPPER_ORIGIN=",
+ sizeof("CODEX_ZSH_EXEC_BRIDGE_WRAPPER_ORIGIN=") - 1) == 0)
+ wrapper_origin_idx = wrapper_envc;
+ wrapper_envc++;
+ }
+ wrapper_envp = zalloc((wrapper_envc + 2) * sizeof(char *));
+ if (wrapper_origin_idx >= 0) {
+ for (wrapper_envc = 0; newenvp[wrapper_envc]; wrapper_envc++)
+ wrapper_envp[wrapper_envc] =
+ wrapper_envc == wrapper_origin_idx ?
+ wrapper_origin_buf : newenvp[wrapper_envc];
+ wrapper_envp[wrapper_envc] = NULL;
+ } else {
+ for (wrapper_envc = 0; newenvp[wrapper_envc]; wrapper_envc++)
+ wrapper_envp[wrapper_envc] = newenvp[wrapper_envc];
+ wrapper_envp[wrapper_envc++] = wrapper_origin_buf;
+ wrapper_envp[wrapper_envc] = NULL;
+ }
+ }
+ pth = exec_wrapper;
+ }
winch_unblock();
- execve(pth, argv, newenvp);
+ execve(pth, exec_argv, newenvp);
+ execve(pth, exec_argv, wrapper_envp);
+ pth = orig_pth;
/* If the execve returns (which in general shouldn't happen), *
* then check for an errno equal to ENOEXEC. This errno is set *
diff --git a/Src/init.c b/Src/init.c
index 20b8cc7fd..b6d5c9c9a 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -59,6 +59,9 @@ int underscoreused;
/**/
int sourcelevel;
+/**/
+int zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_USER_COMMAND;
+
/* the shell tty fd */
/**/
@@ -1450,14 +1453,25 @@ init_signals(void)
void
run_init_scripts(void)
{
+ int old_origin;
+
noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_SIGNAL;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_USER_COMMAND;
if (EMULATION(EMULATE_KSH|EMULATE_SH)) {
- if (islogin)
+ if (islogin) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
source("/etc/profile");
+ zsh_exec_wrapper_origin = old_origin;
+ }
if (unset(PRIVILEGED)) {
- if (islogin)
+ if (islogin) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
sourcehome(".profile");
+ zsh_exec_wrapper_origin = old_origin;
+ }
if (interact) {
noerrs = 2;
@@ -1467,16 +1481,26 @@ run_init_scripts(void)
if (!parsestr(&s)) {
singsub(&s);
noerrs = 0;
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_RC_STARTUP;
source(s);
+ zsh_exec_wrapper_origin = old_origin;
}
}
noerrs = 0;
}
- } else
+ } else {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
source("/etc/suid_profile");
+ zsh_exec_wrapper_origin = old_origin;
+ }
} else {
#ifdef GLOBAL_ZSHENV
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_RC_STARTUP;
source(GLOBAL_ZSHENV);
+ zsh_exec_wrapper_origin = old_origin;
#endif
if (isset(RCS) && unset(PRIVILEGED))
@@ -1492,33 +1516,61 @@ run_init_scripts(void)
}
}
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_RC_STARTUP;
sourcehome(".zshenv");
+ zsh_exec_wrapper_origin = old_origin;
}
if (islogin) {
#ifdef GLOBAL_ZPROFILE
- if (isset(RCS) && isset(GLOBALRCS))
+ if (isset(RCS) && isset(GLOBALRCS)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
source(GLOBAL_ZPROFILE);
+ zsh_exec_wrapper_origin = old_origin;
+ }
#endif
- if (isset(RCS) && unset(PRIVILEGED))
+ if (isset(RCS) && unset(PRIVILEGED)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
sourcehome(".zprofile");
+ zsh_exec_wrapper_origin = old_origin;
+ }
}
if (interact) {
#ifdef GLOBAL_ZSHRC
- if (isset(RCS) && isset(GLOBALRCS))
+ if (isset(RCS) && isset(GLOBALRCS)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_RC_STARTUP;
source(GLOBAL_ZSHRC);
+ zsh_exec_wrapper_origin = old_origin;
+ }
#endif
- if (isset(RCS) && unset(PRIVILEGED))
+ if (isset(RCS) && unset(PRIVILEGED)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_RC_STARTUP;
sourcehome(".zshrc");
+ zsh_exec_wrapper_origin = old_origin;
+ }
}
if (islogin) {
#ifdef GLOBAL_ZLOGIN
- if (isset(RCS) && isset(GLOBALRCS))
+ if (isset(RCS) && isset(GLOBALRCS)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
source(GLOBAL_ZLOGIN);
+ zsh_exec_wrapper_origin = old_origin;
+ }
#endif
- if (isset(RCS) && unset(PRIVILEGED))
+ if (isset(RCS) && unset(PRIVILEGED)) {
+ old_origin = zsh_exec_wrapper_origin;
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP;
sourcehome(".zlogin");
+ zsh_exec_wrapper_origin = old_origin;
+ }
}
}
+ zsh_exec_wrapper_origin = EXEC_WRAPPER_ORIGIN_USER_COMMAND;
noerrexit = 0;
nohistsave = 0;
}
diff --git a/Src/zsh.h b/Src/zsh.h
index 5bda04e88..c0750cf45 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2215,6 +2215,14 @@ enum source_return {
SOURCE_ERROR = 2
};
+enum exec_wrapper_origin {
+ EXEC_WRAPPER_ORIGIN_USER_COMMAND = 0,
+ EXEC_WRAPPER_ORIGIN_LOGIN_STARTUP = 1,
+ EXEC_WRAPPER_ORIGIN_RC_STARTUP = 2
+};
+
+extern int zsh_exec_wrapper_origin;
+
enum noerrexit_bits {
/* Suppress ERR_EXIT and traps: global */
NOERREXIT_EXIT = 1,