Merge pull request #124556 from bergkvist/bergkvist/make-c-wrapper
Generate tiny compiled binary for wrapping executablesmain
commit
9fb7d91888
@ -0,0 +1,384 @@ |
||||
|
||||
set -euo pipefail |
||||
|
||||
# Assert that FILE exists and is executable |
||||
# |
||||
# assertExecutable FILE |
||||
assertExecutable() { |
||||
local file="$1" |
||||
[[ -f "$file" && -x "$file" ]] || \ |
||||
die "Cannot wrap '$file' because it is not an executable file" |
||||
} |
||||
|
||||
# Generate a binary executable wrapper for wrapping an executable. |
||||
# The binary is compiled from generated C-code using gcc. |
||||
# makeWrapper EXECUTABLE OUT_PATH ARGS |
||||
|
||||
# ARGS: |
||||
# --argv0 NAME : set name of executed process to NAME |
||||
# (otherwise it’s called …-wrapped) |
||||
# --inherit-argv0 : the executable inherits argv0 from the wrapper. |
||||
# (use instead of --argv0 '$0') |
||||
# --set VAR VAL : add VAR with value VAL to the executable’s |
||||
# environment |
||||
# --set-default VAR VAL : like --set, but only adds VAR if not already set in |
||||
# the environment |
||||
# --unset VAR : remove VAR from the environment |
||||
# --chdir DIR : change working directory (use instead of --run "cd DIR") |
||||
# --add-flags FLAGS : add FLAGS to invocation of executable |
||||
|
||||
# --prefix ENV SEP VAL : suffix/prefix ENV with VAL, separated by SEP |
||||
# --suffix |
||||
|
||||
# To troubleshoot a binary wrapper after you compiled it, |
||||
# use the `strings` command or open the binary file in a text editor. |
||||
makeWrapper() { |
||||
assertExecutable "$1" |
||||
makeDocumentedCWrapper "$1" "${@:3}" | \ |
||||
@CC@ \ |
||||
-Wall -Werror -Wpedantic \ |
||||
-Os \ |
||||
-x c \ |
||||
-o "$2" - |
||||
} |
||||
|
||||
# Syntax: wrapProgram <PROGRAM> <MAKE-WRAPPER FLAGS...> |
||||
wrapProgram() { |
||||
local prog="$1" |
||||
local hidden |
||||
|
||||
assertExecutable "$prog" |
||||
|
||||
hidden="$(dirname "$prog")/.$(basename "$prog")"-wrapped |
||||
while [ -e "$hidden" ]; do |
||||
hidden="${hidden}_" |
||||
done |
||||
mv "$prog" "$hidden" |
||||
# Silence warning about unexpanded $0: |
||||
# shellcheck disable=SC2016 |
||||
makeWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}" |
||||
} |
||||
|
||||
# Generate source code for the wrapper in such a way that the wrapper inputs |
||||
# will still be readable even after compilation |
||||
# makeDocumentedCWrapper EXECUTABLE ARGS |
||||
# ARGS: same as makeWrapper |
||||
makeDocumentedCWrapper() { |
||||
local src docs |
||||
src=$(makeCWrapper "$@") |
||||
docs=$(docstring "$@") |
||||
printf '%s\n\n' "$src" |
||||
printf '%s\n' "$docs" |
||||
} |
||||
|
||||
# makeCWrapper EXECUTABLE ARGS |
||||
# ARGS: same as makeWrapper |
||||
makeCWrapper() { |
||||
local argv0 inherit_argv0 n params cmd main flagsBefore flags executable length |
||||
local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf |
||||
executable=$(escapeStringLiteral "$1") |
||||
params=("$@") |
||||
length=${#params[*]} |
||||
for ((n = 1; n < length; n += 1)); do |
||||
p="${params[n]}" |
||||
case $p in |
||||
--set) |
||||
cmd=$(setEnv "${params[n + 1]}" "${params[n + 2]}") |
||||
main="$main$cmd"$'\n' |
||||
n=$((n + 2)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n' |
||||
;; |
||||
--set-default) |
||||
cmd=$(setDefaultEnv "${params[n + 1]}" "${params[n + 2]}") |
||||
main="$main$cmd"$'\n' |
||||
uses_stdio=1 |
||||
uses_assert_success=1 |
||||
n=$((n + 2)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n' |
||||
;; |
||||
--unset) |
||||
cmd=$(unsetEnv "${params[n + 1]}") |
||||
main="$main$cmd"$'\n' |
||||
uses_stdio=1 |
||||
uses_assert_success=1 |
||||
n=$((n + 1)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n' |
||||
;; |
||||
--prefix) |
||||
cmd=$(setEnvPrefix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}") |
||||
main="$main$cmd"$'\n' |
||||
uses_prefix=1 |
||||
uses_asprintf=1 |
||||
uses_stdio=1 |
||||
uses_assert_success=1 |
||||
uses_assert=1 |
||||
n=$((n + 3)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n' |
||||
;; |
||||
--suffix) |
||||
cmd=$(setEnvSuffix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}") |
||||
main="$main$cmd"$'\n' |
||||
uses_suffix=1 |
||||
uses_asprintf=1 |
||||
uses_stdio=1 |
||||
uses_assert_success=1 |
||||
uses_assert=1 |
||||
n=$((n + 3)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n' |
||||
;; |
||||
--chdir) |
||||
cmd=$(changeDir "${params[n + 1]}") |
||||
main="$main$cmd"$'\n' |
||||
uses_stdio=1 |
||||
uses_assert_success=1 |
||||
n=$((n + 1)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n' |
||||
;; |
||||
--add-flags) |
||||
flags="${params[n + 1]}" |
||||
flagsBefore="$flagsBefore $flags" |
||||
uses_assert=1 |
||||
n=$((n + 1)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n' |
||||
;; |
||||
--argv0) |
||||
argv0=$(escapeStringLiteral "${params[n + 1]}") |
||||
inherit_argv0= |
||||
n=$((n + 1)) |
||||
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n' |
||||
;; |
||||
--inherit-argv0) |
||||
# Whichever comes last of --argv0 and --inherit-argv0 wins |
||||
inherit_argv0=1 |
||||
;; |
||||
*) # Using an error macro, we will make sure the compiler gives an understandable error message |
||||
main="$main#error makeCWrapper: Unknown argument ${p}"$'\n' |
||||
;; |
||||
esac |
||||
done |
||||
# shellcheck disable=SC2086 |
||||
[ -z "$flagsBefore" ] || main="$main"${main:+$'\n'}$(addFlags $flagsBefore)$'\n'$'\n' |
||||
[ -z "$inherit_argv0" ] && main="${main}argv[0] = \"${argv0:-${executable}}\";"$'\n' |
||||
main="${main}return execv(\"${executable}\", argv);"$'\n' |
||||
|
||||
[ -z "$uses_asprintf" ] || printf '%s\n' "#define _GNU_SOURCE /* See feature_test_macros(7) */" |
||||
printf '%s\n' "#include <unistd.h>" |
||||
printf '%s\n' "#include <stdlib.h>" |
||||
[ -z "$uses_assert" ] || printf '%s\n' "#include <assert.h>" |
||||
[ -z "$uses_stdio" ] || printf '%s\n' "#include <stdio.h>" |
||||
[ -z "$uses_assert_success" ] || printf '\n%s\n' "#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)" |
||||
[ -z "$uses_prefix" ] || printf '\n%s\n' "$(setEnvPrefixFn)" |
||||
[ -z "$uses_suffix" ] || printf '\n%s\n' "$(setEnvSuffixFn)" |
||||
printf '\n%s' "int main(int argc, char **argv) {" |
||||
printf '\n%s' "$(indent4 "$main")" |
||||
printf '\n%s\n' "}" |
||||
} |
||||
|
||||
addFlags() { |
||||
local result n flag flags var |
||||
var="argv_tmp" |
||||
flags=("$@") |
||||
for ((n = 0; n < ${#flags[*]}; n += 1)); do |
||||
flag=$(escapeStringLiteral "${flags[$n]}") |
||||
result="$result${var}[$((n+1))] = \"$flag\";"$'\n' |
||||
done |
||||
printf '%s\n' "char **$var = calloc($((n+1)) + argc, sizeof(*$var));" |
||||
printf '%s\n' "assert($var != NULL);" |
||||
printf '%s\n' "${var}[0] = argv[0];" |
||||
printf '%s' "$result" |
||||
printf '%s\n' "for (int i = 1; i < argc; ++i) {" |
||||
printf '%s\n' " ${var}[$n + i] = argv[i];" |
||||
printf '%s\n' "}" |
||||
printf '%s\n' "${var}[$n + argc] = NULL;" |
||||
printf '%s\n' "argv = $var;" |
||||
} |
||||
|
||||
# chdir DIR |
||||
changeDir() { |
||||
local dir |
||||
dir=$(escapeStringLiteral "$1") |
||||
printf '%s' "assert_success(chdir(\"$dir\"));" |
||||
} |
||||
|
||||
# prefix ENV SEP VAL |
||||
setEnvPrefix() { |
||||
local env sep val |
||||
env=$(escapeStringLiteral "$1") |
||||
sep=$(escapeStringLiteral "$2") |
||||
val=$(escapeStringLiteral "$3") |
||||
printf '%s' "set_env_prefix(\"$env\", \"$sep\", \"$val\");" |
||||
assertValidEnvName "$1" |
||||
} |
||||
|
||||
# suffix ENV SEP VAL |
||||
setEnvSuffix() { |
||||
local env sep val |
||||
env=$(escapeStringLiteral "$1") |
||||
sep=$(escapeStringLiteral "$2") |
||||
val=$(escapeStringLiteral "$3") |
||||
printf '%s' "set_env_suffix(\"$env\", \"$sep\", \"$val\");" |
||||
assertValidEnvName "$1" |
||||
} |
||||
|
||||
# setEnv KEY VALUE |
||||
setEnv() { |
||||
local key value |
||||
key=$(escapeStringLiteral "$1") |
||||
value=$(escapeStringLiteral "$2") |
||||
printf '%s' "putenv(\"$key=$value\");" |
||||
assertValidEnvName "$1" |
||||
} |
||||
|
||||
# setDefaultEnv KEY VALUE |
||||
setDefaultEnv() { |
||||
local key value |
||||
key=$(escapeStringLiteral "$1") |
||||
value=$(escapeStringLiteral "$2") |
||||
printf '%s' "assert_success(setenv(\"$key\", \"$value\", 0));" |
||||
assertValidEnvName "$1" |
||||
} |
||||
|
||||
# unsetEnv KEY |
||||
unsetEnv() { |
||||
local key |
||||
key=$(escapeStringLiteral "$1") |
||||
printf '%s' "assert_success(unsetenv(\"$key\"));" |
||||
assertValidEnvName "$1" |
||||
} |
||||
|
||||
# Makes it safe to insert STRING within quotes in a C String Literal. |
||||
# escapeStringLiteral STRING |
||||
escapeStringLiteral() { |
||||
local result |
||||
result=${1//$'\\'/$'\\\\'} |
||||
result=${result//\"/'\"'} |
||||
result=${result//$'\n'/"\n"} |
||||
result=${result//$'\r'/"\r"} |
||||
printf '%s' "$result" |
||||
} |
||||
|
||||
# Indents every non-empty line by 4 spaces. To avoid trailing whitespace, we don't indent empty lines |
||||
# indent4 TEXT_BLOCK |
||||
indent4() { |
||||
printf '%s' "$1" | awk '{ if ($0 != "") { print " "$0 } else { print $0 }}' |
||||
} |
||||
|
||||
assertValidEnvName() { |
||||
case "$1" in |
||||
*=*) printf '\n%s\n' "#error Illegal environment variable name \`$1\` (cannot contain \`=\`)";; |
||||
"") printf '\n%s\n' "#error Environment variable name can't be empty.";; |
||||
esac |
||||
} |
||||
|
||||
setEnvPrefixFn() { |
||||
printf '%s' "\ |
||||
void set_env_prefix(char *env, char *sep, char *prefix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, \"%s%s%s\", prefix, sep, existing)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, prefix, 1)); |
||||
} |
||||
} |
||||
" |
||||
} |
||||
|
||||
setEnvSuffixFn() { |
||||
printf '%s' "\ |
||||
void set_env_suffix(char *env, char *sep, char *suffix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, \"%s%s%s\", existing, sep, suffix)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, suffix, 1)); |
||||
} |
||||
} |
||||
" |
||||
} |
||||
|
||||
# Embed a C string which shows up as readable text in the compiled binary wrapper |
||||
# documentationString ARGS |
||||
docstring() { |
||||
printf '%s' "const char * DOCSTRING = \"$(escapeStringLiteral " |
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------ |
||||
# The C-code for this binary wrapper has been generated using the following command: |
||||
|
||||
|
||||
makeCWrapper $(formatArgs "$@") |
||||
|
||||
|
||||
# (Use \`nix-shell -p makeBinaryWrapper\` to get access to makeCWrapper in your shell) |
||||
# ------------------------------------------------------------------------------------ |
||||
|
||||
|
||||
")\";" |
||||
} |
||||
|
||||
# formatArgs EXECUTABLE ARGS |
||||
formatArgs() { |
||||
printf '%s' "$1" |
||||
shift |
||||
while [ $# -gt 0 ]; do |
||||
case "$1" in |
||||
--set) |
||||
formatArgsLine 2 "$@" |
||||
shift 2 |
||||
;; |
||||
--set-default) |
||||
formatArgsLine 2 "$@" |
||||
shift 2 |
||||
;; |
||||
--unset) |
||||
formatArgsLine 1 "$@" |
||||
shift 1 |
||||
;; |
||||
--prefix) |
||||
formatArgsLine 3 "$@" |
||||
shift 3 |
||||
;; |
||||
--suffix) |
||||
formatArgsLine 3 "$@" |
||||
shift 3 |
||||
;; |
||||
--chdir) |
||||
formatArgsLine 1 "$@" |
||||
shift 1 |
||||
;; |
||||
--add-flags) |
||||
formatArgsLine 1 "$@" |
||||
shift 1 |
||||
;; |
||||
--argv0) |
||||
formatArgsLine 1 "$@" |
||||
shift 1 |
||||
;; |
||||
--inherit-argv0) |
||||
formatArgsLine 0 "$@" |
||||
;; |
||||
esac |
||||
shift |
||||
done |
||||
printf '%s\n' "" |
||||
} |
||||
|
||||
# formatArgsLine ARG_COUNT ARGS |
||||
formatArgsLine() { |
||||
local ARG_COUNT LENGTH |
||||
ARG_COUNT=$1 |
||||
LENGTH=$# |
||||
shift |
||||
printf '%s' $' \\\n '"$1" |
||||
shift |
||||
while [ "$ARG_COUNT" -gt $((LENGTH - $# - 2)) ]; do |
||||
printf ' %s' "${1@Q}" |
||||
shift |
||||
done |
||||
} |
@ -0,0 +1,21 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
|
||||
int main(int argc, char **argv) { |
||||
char **argv_tmp = calloc(5 + argc, sizeof(*argv_tmp)); |
||||
assert(argv_tmp != NULL); |
||||
argv_tmp[0] = argv[0]; |
||||
argv_tmp[1] = "-x"; |
||||
argv_tmp[2] = "-y"; |
||||
argv_tmp[3] = "-z"; |
||||
argv_tmp[4] = "-abc"; |
||||
for (int i = 1; i < argc; ++i) { |
||||
argv_tmp[4 + i] = argv[i]; |
||||
} |
||||
argv_tmp[4 + argc] = NULL; |
||||
argv = argv_tmp; |
||||
|
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,2 @@ |
||||
--add-flags "-x -y -z" \ |
||||
--add-flags -abc |
@ -0,0 +1,6 @@ |
||||
CWD=SUBST_CWD |
||||
SUBST_ARGV0 |
||||
-x |
||||
-y |
||||
-z |
||||
-abc |
@ -0,0 +1,7 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
|
||||
int main(int argc, char **argv) { |
||||
argv[0] = "alternative-name"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1 @@ |
||||
--argv0 alternative-name |
@ -0,0 +1,2 @@ |
||||
CWD=SUBST_CWD |
||||
alternative-name |
@ -0,0 +1,7 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
|
||||
int main(int argc, char **argv) { |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,2 @@ |
||||
CWD=SUBST_CWD |
||||
SUBST_ARGV0 |
@ -0,0 +1,11 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
int main(int argc, char **argv) { |
||||
assert_success(chdir("/tmp/foo")); |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1 @@ |
||||
--chdir /tmp/foo |
@ -0,0 +1,2 @@ |
||||
CWD=/tmp/foo |
||||
SUBST_ARGV0 |
@ -0,0 +1,53 @@ |
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
void set_env_prefix(char *env, char *sep, char *prefix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, prefix, 1)); |
||||
} |
||||
} |
||||
|
||||
void set_env_suffix(char *env, char *sep, char *suffix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, suffix, 1)); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
assert_success(setenv("MESSAGE", "HELLO", 0)); |
||||
set_env_prefix("PATH", ":", "/usr/bin/"); |
||||
set_env_suffix("PATH", ":", "/usr/local/bin/"); |
||||
putenv("MESSAGE2=WORLD"); |
||||
|
||||
char **argv_tmp = calloc(4 + argc, sizeof(*argv_tmp)); |
||||
assert(argv_tmp != NULL); |
||||
argv_tmp[0] = argv[0]; |
||||
argv_tmp[1] = "-x"; |
||||
argv_tmp[2] = "-y"; |
||||
argv_tmp[3] = "-z"; |
||||
for (int i = 1; i < argc; ++i) { |
||||
argv_tmp[3 + i] = argv[i]; |
||||
} |
||||
argv_tmp[3 + argc] = NULL; |
||||
argv = argv_tmp; |
||||
|
||||
argv[0] = "my-wrapper"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,6 @@ |
||||
--argv0 my-wrapper \ |
||||
--set-default MESSAGE HELLO \ |
||||
--prefix PATH : /usr/bin/ \ |
||||
--suffix PATH : /usr/local/bin/ \ |
||||
--add-flags "-x -y -z" \ |
||||
--set MESSAGE2 WORLD |
@ -0,0 +1,8 @@ |
||||
MESSAGE=HELLO |
||||
PATH=/usr/bin/:/usr/local/bin/ |
||||
MESSAGE2=WORLD |
||||
CWD=SUBST_CWD |
||||
my-wrapper |
||||
-x |
||||
-y |
||||
-z |
@ -0,0 +1,54 @@ |
||||
{ lib, coreutils, python3, gcc, writeText, writeScript, runCommand, makeBinaryWrapper }: |
||||
|
||||
let |
||||
env = { nativeBuildInputs = [ makeBinaryWrapper ]; }; |
||||
envCheck = runCommand "envcheck" env '' |
||||
${gcc}/bin/cc -Wall -Werror -Wpedantic -o $out ${./envcheck.c} |
||||
''; |
||||
makeGoldenTest = testname: runCommand "test-wrapper_${testname}" env '' |
||||
mkdir -p /tmp/foo |
||||
|
||||
params=$(<"${./.}/${testname}.cmdline") |
||||
eval "makeCWrapper /send/me/flags $params" > wrapper.c |
||||
|
||||
diff wrapper.c "${./.}/${testname}.c" |
||||
|
||||
if [ -f "${./.}/${testname}.env" ]; then |
||||
eval "makeWrapper ${envCheck} wrapped $params" |
||||
env -i ./wrapped > env.txt |
||||
sed "s#SUBST_ARGV0#${envCheck}#;s#SUBST_CWD#$PWD#" \ |
||||
"${./.}/${testname}.env" > golden-env.txt |
||||
if ! diff env.txt golden-env.txt; then |
||||
echo "env/argv should be:" |
||||
cat golden-env.txt |
||||
echo "env/argv output is:" |
||||
cat env.txt |
||||
exit 1 |
||||
fi |
||||
else |
||||
# without a golden env, we expect the wrapper compilation to fail |
||||
! eval "makeWrapper ${envCheck} wrapped $params" &> error.txt |
||||
fi |
||||
|
||||
cp wrapper.c $out |
||||
''; |
||||
tests = let |
||||
names = [ |
||||
"add-flags" |
||||
"argv0" |
||||
"basic" |
||||
"chdir" |
||||
"combination" |
||||
"env" |
||||
"inherit-argv0" |
||||
"invalid-env" |
||||
"prefix" |
||||
"suffix" |
||||
]; |
||||
f = name: lib.nameValuePair name (makeGoldenTest name); |
||||
in builtins.listToAttrs (builtins.map f names); |
||||
in writeText "make-binary-wrapper-test" '' |
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: '' |
||||
"${test.name}" "${test}" |
||||
'') tests)} |
||||
'' // tests |
@ -0,0 +1,14 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
int main(int argc, char **argv) { |
||||
putenv("PART1=HELLO"); |
||||
assert_success(setenv("PART2", "WORLD", 0)); |
||||
assert_success(unsetenv("SOME_OTHER_VARIABLE")); |
||||
putenv("PART3=\"!!\n\""); |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,4 @@ |
||||
--set PART1 HELLO \ |
||||
--set-default PART2 WORLD \ |
||||
--unset SOME_OTHER_VARIABLE \ |
||||
--set PART3 $'"!!\n"' |
@ -0,0 +1,6 @@ |
||||
PART1=HELLO |
||||
PART2=WORLD |
||||
PART3="!! |
||||
" |
||||
CWD=SUBST_CWD |
||||
SUBST_ARGV0 |
@ -0,0 +1,22 @@ |
||||
#include <limits.h> |
||||
#include <stdio.h> |
||||
#include <unistd.h> |
||||
|
||||
int main(int argc, char **argv, char **envp) { |
||||
for (char **env = envp; *env != 0; ++env) { |
||||
puts(*env); |
||||
} |
||||
|
||||
char cwd[PATH_MAX]; |
||||
if (getcwd(cwd, sizeof(cwd))) { |
||||
printf("CWD=%s\n", cwd); |
||||
} else { |
||||
perror("getcwd() error"); |
||||
return 1; |
||||
} |
||||
|
||||
for (int i=0; i < argc; ++i) { |
||||
puts(argv[i]); |
||||
} |
||||
return 0; |
||||
} |
@ -0,0 +1,6 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
|
||||
int main(int argc, char **argv) { |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1 @@ |
||||
--inherit-argv0 |
@ -0,0 +1,2 @@ |
||||
CWD=SUBST_CWD |
||||
./wrapped |
@ -0,0 +1,14 @@ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
int main(int argc, char **argv) { |
||||
putenv("==TEST1"); |
||||
#error Illegal environment variable name `=` (cannot contain `=`) |
||||
assert_success(setenv("", "TEST2", 0)); |
||||
#error Environment variable name can't be empty. |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,2 @@ |
||||
--set "=" "TEST1" \ |
||||
--set-default "" "TEST2" |
@ -0,0 +1,26 @@ |
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
void set_env_prefix(char *env, char *sep, char *prefix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, prefix, 1)); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
set_env_prefix("PATH", ":", "/usr/bin/"); |
||||
set_env_prefix("PATH", ":", "/usr/local/bin/"); |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,2 @@ |
||||
--prefix PATH : /usr/bin/ \ |
||||
--prefix PATH : /usr/local/bin/ |
@ -0,0 +1,3 @@ |
||||
PATH=/usr/local/bin/:/usr/bin/ |
||||
CWD=SUBST_CWD |
||||
SUBST_ARGV0 |
@ -0,0 +1,26 @@ |
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */ |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
|
||||
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0) |
||||
|
||||
void set_env_suffix(char *env, char *sep, char *suffix) { |
||||
char *existing = getenv(env); |
||||
if (existing) { |
||||
char *val; |
||||
assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix)); |
||||
assert_success(setenv(env, val, 1)); |
||||
free(val); |
||||
} else { |
||||
assert_success(setenv(env, suffix, 1)); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
set_env_suffix("PATH", ":", "/usr/bin/"); |
||||
set_env_suffix("PATH", ":", "/usr/local/bin/"); |
||||
argv[0] = "/send/me/flags"; |
||||
return execv("/send/me/flags", argv); |
||||
} |
@ -0,0 +1,2 @@ |
||||
--suffix PATH : /usr/bin/ \ |
||||
--suffix PATH : /usr/local/bin/ |
@ -0,0 +1,3 @@ |
||||
PATH=/usr/bin/:/usr/local/bin/ |
||||
CWD=SUBST_CWD |
||||
SUBST_ARGV0 |
Loading…
Reference in new issue