| #!/usr/bin/env bash |
| # |
| # Script to install host system binaries along with required libraries. |
| # |
| # Copyright (C) 2012-2017 Jo-Philipp Wich <jo@mein.io> |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| DIR="$1"; shift |
| |
| _cp() { |
| cp ${VERBOSE:+-v} -L "$1" "$2" || { |
| echo "cp($1 $2) failed" >&2 |
| exit 1 |
| } |
| } |
| |
| _mv() { |
| mv ${VERBOSE:+-v} "$1" "$2" || { |
| echo "mv($1 $2) failed" >&2 |
| exit 1 |
| } |
| } |
| |
| _md() { |
| mkdir ${VERBOSE:+-v} -p "$1" || { |
| echo "mkdir($1) failed" >&2 |
| exit 2 |
| } |
| } |
| |
| _ln() { |
| ln ${VERBOSE:+-v} -sf "$1" "$2" || { |
| echo "ln($1 $2) failed" >&2 |
| exit 3 |
| } |
| } |
| |
| _relpath() { |
| local base="$(readlink -f "$1")" |
| local dest="$(readlink -f "$2")" |
| local up |
| |
| [ -d "$base" ] || base="${base%/*}" |
| [ -d "$dest" ] || dest="${dest%/*}" |
| |
| while true; do |
| case "$base" |
| in "$dest"/*) |
| echo "$up/${base#$dest/}" |
| break |
| ;; |
| *) |
| dest="${dest%/*}" |
| up="${up:+$up/}.." |
| ;; |
| esac |
| done |
| } |
| |
| _runas_so() { |
| cat <<-EOT | ${CC:-gcc} -x c -fPIC -shared -o "$1" - |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| int mangle_arg0(int argc, char **argv, char **env) { |
| char *arg0 = getenv("RUNAS_ARG0"); |
| |
| if (arg0) { |
| argv[0] = arg0; |
| unsetenv("RUNAS_ARG0"); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef __APPLE__ |
| __attribute__((section("__DATA,__mod_init_func"))) |
| #else |
| __attribute__((section(".init_array"))) |
| #endif |
| static void *mangle_arg0_constructor = &mangle_arg0; |
| EOT |
| |
| [ -x "$1" ] || { |
| echo "compiling preload library failed" >&2 |
| exit 5 |
| } |
| } |
| |
| _patch_ldso() { |
| _cp "$1" "$1.patched" |
| sed -i -e 's,/\(usr\|lib\|etc\)/,/###/,g' "$1.patched" |
| |
| if "$1.patched" 2>&1 | grep -q -- --library-path; then |
| _mv "$1.patched" "$1" |
| else |
| echo "binary patched ${1##*/} not executable, using original" >&2 |
| rm -f "$1.patched" |
| fi |
| } |
| |
| _patch_glibc() { |
| _cp "$1" "$1.patched" |
| sed -i -e 's,/usr/\(\(lib\|share\)/locale\),/###/\1,g' "$1.patched" |
| |
| if "$1.patched" 2>&1 | grep -q -- GNU; then |
| _mv "$1.patched" "$1" |
| else |
| echo "binary patched ${1##*/} not executable, using original" >&2 |
| rm -f "$1.patched" |
| fi |
| } |
| |
| should_be_patched() { |
| local bin="$1" |
| |
| [ -x "$bin" ] || return 1 |
| |
| case "$bin" in |
| *.so|*.so.[0-9]*) |
| return 1 |
| ;; |
| *) |
| file "$bin" | grep -sqE "ELF.*(executable|interpreter)" && return 0 |
| ;; |
| esac |
| |
| return 1 |
| } |
| |
| for LDD in ${PATH//://ldd }/ldd; do |
| "$LDD" --version >/dev/null 2>/dev/null && break |
| LDD="" |
| done |
| |
| [ -n "$LDD" -a -x "$LDD" ] || LDD= |
| |
| for BIN in "$@"; do |
| [ -n "$BIN" -a -n "$DIR" ] || { |
| echo "Usage: $0 <destdir> <executable> ..." >&2 |
| exit 1 |
| } |
| |
| [ ! -d "$DIR/lib" ] && { |
| _md "$DIR/lib" |
| _md "$DIR/usr" |
| _ln "../lib" "$DIR/usr/lib" |
| } |
| |
| [ ! -x "$DIR/lib/runas.so" ] && { |
| _runas_so "$DIR/lib/runas.so" |
| } |
| |
| LDSO="" |
| |
| [ -n "$LDD" ] && should_be_patched "$BIN" && { |
| for token in $("$LDD" "$BIN" 2>/dev/null); do |
| case "$token" in */*.so*) |
| dest="$DIR/lib/${token##*/}" |
| ddir="${dest%/*}" |
| |
| case "$token" in |
| */ld-*.so*) LDSO="${token##*/}" ;; |
| esac |
| |
| [ -f "$token" -a ! -f "$dest" ] && { |
| _md "$ddir" |
| _cp "$token" "$dest" |
| case "$token" in |
| */ld-*.so*) _patch_ldso "$dest" ;; |
| */libc.so.6) _patch_glibc "$dest" ;; |
| esac |
| } |
| ;; esac |
| done |
| } |
| |
| # is a dynamically linked executable |
| if [ -n "$LDSO" ]; then |
| echo "Bundling ${BIN##*/}" |
| |
| RUNDIR="$(readlink -f "$BIN")"; RUNDIR="${RUNDIR%/*}" |
| RUN="${LDSO#ld-}"; RUN="run-${RUN%%.so*}.sh" |
| REL="$(_relpath "$DIR/lib" "$BIN")" |
| |
| _mv "$BIN" "$RUNDIR/.${BIN##*/}.bin" |
| |
| cat <<-EOF > "$BIN" |
| #!/usr/bin/env bash |
| dir="\$(dirname "\$0")" |
| export RUNAS_ARG0="\$0" |
| export LD_PRELOAD="\${LD_PRELOAD:+\$LD_PRELOAD:}\$dir/${REL:+$REL/}runas.so" |
| exec "\$dir/${REL:+$REL/}$LDSO" --library-path "\$dir/${REL:+$REL/}" "\$dir/.${BIN##*/}.bin" "\$@" |
| EOF |
| |
| chmod ${VERBOSE:+-v} 0755 "$BIN" |
| fi |
| done |