Repository URL to install this package:
|
Version:
0.4.2+3 ▾
|
#!/bin/bash
ME=${0##*/}
: ${PATH_LIST:=/usr/local/bin /usr/sbin /usr/bin /sbin /bin}
: ${BIN_SUBDIR:=bin}
: ${LIB_SUBDIR:=lib}
: ${DEFAULT_PROG_LIST:=ntfs-3g eject kmod}
: ${ENCRYPT_PROG_LIST:=cryptsetup}
: ${DEFAULT_KMOD_LIST:=depmod insmod lsmod modinfo modprobe}
X86_64_DIR=/lib/x86_64-linux-gnu
VAR_LIST="PATH_LIST BIN_SUBDIR LIB_SUBDIR"
VAR_LIST+=" DEFAULT_PROG_LIST ENCRYPT_PROG_LIST DEFAULT_KMOD_LIST"
#PRETEND=true
usage() {
cat <<Usage
usage: $ME [options] <[programs]
Copy dynamic linked programs and their supporting libraries
into the /bin and /lib directories of the initrd.
Options:
-C --clean Remove existing libraries and programs
-c --create Create the --to directory if it doesn't exist
-e --encrypt Add cryptsetup to built-in list of programs
-f --from=<dir> get libs and programs from under <dir>
-F --force Skip missing programs
-h --help Show this usage
-n --no-color Turn off text colors
-p --pretend Don't actually copy anything
-q --quiet Supress modprobe warnings
-s --show Show default values of environment variables
-t --to=<dir> Copy to /bin and /lib under <dir>
-v --verbose Show more information
A --to directory or a from --directory must be specified.
Short options can be stacked. Example:
$ME -cnp
Usage
exit ${1:-0}
}
#------------------------------------------------------------------------------
# callback routine for evaluating command line args
#------------------------------------------------------------------------------
eval_argument() {
local arg=$1 val=$2
case $arg in
-create|c) CREATE=true ;;
-clean|C) DO_CLEAN=true ;;
-encrypt|e) ADD_ENCRYPT=true ;;
-from|f) FROM_DIR=$val ;;
-force|F) FORCE=true ;;
-from=*) FROM_DIR=$val ;;
-help|h) usage ;;
-no-color|n) NO_COLOR=true ;;
-pretend|p) PRETEND=true ;;
-quiet|q) QUIET=true ;;
-show|s) do_show ; exit 0 ;;
-to|t) TO_DIR=$val ;;
-to=*) TO_DIR=$val ;;
-verbose|v) VERBOSE=true ;;
*) fatal "Unknown parameter -$arg" ;;
esac
}
#------------------------------------------------------------------------------
# callback routine for saying which cli args take a parameter
#------------------------------------------------------------------------------
takes_param() {
case $1 in
-from|f) return 0 ;;
-to|t) return 0 ;;
esac
return 1
}
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
main() {
[ $# -eq 0 ] && usage 0
local SHIFT SHORT_STACK="cCefFhnpqtv"
local SUDO
[ "$UID" = 1 ] || SUDO=sudo
read_params "$@"
shift $SHIFT
[ -n "$NO_COLOR" ] || set_colors
local prog_list abs_prog_list
case $# in
0) prog_list=$DEFAULT_PROG_LIST ;;
*) prog_list="$*"
qsay "Installing %s program(s) from command line" $# ;;
esac
if [ $# -eq 0 -a -n "$ADD_ENCRYPT$DO_CLEAN" ]; then
#qsay "Adding encryption programs"
prog_list="$prog_list $ENCRYPT_PROG_LIST"
fi
: ${TO_DIR:=.}
[ "$CREATE" ] && mkdir -p "$TO_DIR"
test -e "$TO_DIR" || fatal "The --to directory %s does not exist. Use --create to have it created" "$TO_DIR"
local bin_dir=$TO_DIR/$BIN_SUBDIR lib_dir=$TO_DIR/$LIB_SUBDIR
if [ "$DO_CLEAN" ]; then
qsay "Cleaning out all libraries and these program(s): %s" "$prog_list"
do_clean "$TO_DIR" "$lib_dir" "$bin_dir" "$prog_list"
exit 0
fi
test -d "$TO_DIR" || fatal "The --to directory %s parameter is not a directoryparameter is not a directory." "$TO_DIR"
# Find the absolute path to each program under FROM_DIR
local prog path abs_prog abs_prog_list missing_progs
for prog in $prog_list; do
local found=
for path in $PATH_LIST; do
abs_prog=$path/$prog
test -x "${FROM_DIR%/}"/$abs_prog || continue
found=true
abs_prog_list="$abs_prog_list $abs_prog"
break
done
[ -z "$found" ] && missing_progs="$missing_progs${missing_progs:+ }$prog"
done
[ -n "$missing_progs" ] && warn_err "The following program(s) were not found: %s" "$missing_progs"
if [ "$VERBOSE" ]; then
say "Full path to programs under %s:" "${FROM_DIR:-/}"
for prog in $abs_prog_list; do
say " %s" "$prog"
done
fi
my_mkdir "$bin_dir"
my_mkdir "$lib_dir"
# Copy each program to the bin directory ...
# And ind the libraries needed for each program
for abs_prog in $abs_prog_list; do
local from_prog="$FROM_DIR$abs_prog"
local basename_prog=$(basename "$abs_prog")
qsay " add program: %s" "$(basename $abs_prog)"
[ -e "$bin_dir/$basename_prog" ] && pretend rm "$bin_dir/$basename_prog"
pretend cp "$from_prog" "$bin_dir/$basename_prog"
# kmod handling
if [ "$basename_prog" = kmod ] && [ -x "$bin_dir/kmod" ]; then
local kmods="$DEFAULT_KMOD_LIST"
local mod
for mod in $kmods; do
pretend ln -nsf kmod "$bin_dir"/$mod
done
fi
local raw_libs
if [ -n "$FROM_DIR" ]; then
local LINUX_ARG=$(get_linux_arg "$from_prog")
raw_libs=$($SUDO $LINUX_ARG chroot "$FROM_DIR" /usr/bin/ldd $abs_prog)
else
raw_libs=$(ldd $abs_prog)
fi
#echo "$raw_libs"
local libs=$(echo "$raw_libs" | sed -nr "s/.*=>\s*([^ ]+)\s.*/\1/p")
if [ -z "$libs" ]; then
warn_err "No libs found for %s" "$(basename "$abs_prog")"
continue
fi
#echo -e "$libs"
local lib
# luks2 handling - needs libgcc_s.so.*
if [ "${abs_prog##*/}" = "cryptsetup" ]; then
local extra libdir libgcc libname
for lib in $libs; do
libdir=${lib%/*};
libname=${lib##*/}; libname=${libname%%.*};
[ -n "${libname##libcryptsetup*}" ] && continue
libgcc="libgcc_s.so.*"
done
[ -n "${libgcc}" ] && extra=( $(cd "${FROM_DIR:-.}"; find "${FROM_DIR:+.}"${libdir} -type f -name "${libgcc}") )
echo libs+=" ${extra[*]#.}"
libs+=" ${extra[*]}"
fi
# For each library, copy the library and the symlink to the lib directory
for lib in $libs; do
copy_symlink "$FROM_DIR" "$lib" "$lib_dir"
done
# copy in the ld-linux program specifed by the program
ld_linux=$(echo "$raw_libs" | sed -nr "s@^\s*(/lib[^ ]*/ld-linux[^ ]*) .*@\1@p")
[ -z "$ld_linux" ] && continue
local ld_dir="$TO_DIR"$(dirname "$ld_linux")
echo "ld_linux: $ld_linux"
if [ "${ld_dir##*/}" = lib64 ]; then
my_mkdir "$ld_dir"
pretend cp -a "$FROM_DIR"$ld_linux $ld_dir
ld_dir=${ld_dir%/lib64}$X86_64_DIR
fi
echo ld_dir: $ld_dir
my_mkdir "$ld_dir"
copy_symlink "$FROM_DIR" "$ld_linux" "$ld_dir"
done
}
#------------------------------------------------------------------------------
# Copy a symlink and what it points to into dest_dir. This code seems too
# complicated for what it does.
#------------------------------------------------------------------------------
copy_symlink() {
local from_dir=${1:-/} symlink=$2 dest_dir=$3
# This works for relative symlinks and absolute symlinks that are also
# exist on the host
local real_file=$($SUDO $LINUX_ARG chroot "$from_dir" readlink -f $symlink)
# echo -e " symlink: $symlink\n real_file: $real_file"
# If it was not really a symlink then just copy and return
if [ -z "$real_file" -o "$real_file" = "$symlink" ]; then
local dest_file="$dest_dir"/$(basename $symlink)
test -e "$dest_file" || pretend cp "$from_dir$symlink" "$dest_file" \
|| fatal "Failed to copy:\n %s -->\n %s" "$from_dir$symlink" "$dest_file"
return
fi
# Otherwise, copy the file and then make a relative symlink to it
local dest_file="$dest_dir"/$(basename $real_file)
test -e "$dest_file" || pretend cp -a "$from_dir$real_file" "$dest_file" \
|| fatal "Failed to copy:\n %s -->\n %s" "$from_dir$real_file" "$dest_file"
local sym_targ=$(basename $real_file)
local sym_sorc="$dest_dir"/$(basename $symlink)
test -e "$sym_sorc" || pretend ln -s $sym_targ "$sym_sorc" \
|| fatal "Failed to create symlink at:\n %s" "$sym_sorc"
}
#------------------------------------------------------------------------------
# We may need to run "linux32" or "linux64" when we do the chroot to find
# dynamic libs.
#------------------------------------------------------------------------------
get_linux_arg() {
local prog=$1
local need_64 new_arch=linux32
if file "$prog" | grep -q x86.64; then
need_64=true
new_arch=linux64
fi
local have_64
uname -m | grep -q x86.64 && have_64=true
local linux_arg
[ "$need_64" != "$have_64" ] && linux_arg=$new_arch
echo "$linux_arg"
}
#------------------------------------------------------------------------------
# Try to remove all libs and binaries that were added in. We actually try to
# remove ALL libs since mismatched libs will tend to waste space and cause
# trouble.
#------------------------------------------------------------------------------
do_clean() {
local to_dir=$1 lib_dir=$2 bin_dir=$3 prog_list=$4
local prog
local restore=""
for prog in $prog_list; do
local busybox="$bin_dir"/busybox
local full="$bin_dir/$prog"
test -e "$full" && pretend rm -f "$full"
if [ -x "$busybox" ] && "$busybox" --list | grep -q "^$prog$"; then
restore+=" $prog"
fi
[ "$prog" = kmod ] || continue
if [ ! -x "$bin_dir/kmod" ] && [ -x "$busybox" ]; then
local kmods="$DEFAULT_KMOD_LIST"
local mod
for mod in $kmods; do
[ x"$(readlink "$bin_dir"/$mod)" = xbusybox ] && continue
restore+=" $mod"
done
fi
done
# restore busybox links
local link
for link in $restore; do
local full="$bin_dir/$link"
if [ -x "$busybox" ] && "$busybox" --list | grep -q "^$link$"; then
pretend ln -nsf busybox "$bin_dir"/$link
fi
done
local lib
for lib in "$to_dir"/lib*/{lib,ld}*.so* "$to_dir"/$X86_64_DIR/ld-*.so*; do
[ -e "$lib" -o -L "$lib" ] && pretend rm -f "$lib"
done
local dir
for dir in "$to_dir"/lib*/ $bin_dir "$to_dir"$X86_64_DIR; do
test -d "$dir" || continue
[ -z "$(ls -A "$dir" 2>/dev/null)" ] && pretend rmdir "$dir"
done
}
#------------------------------------------------------------------------------
# Show default values of our environment variables
#------------------------------------------------------------------------------
do_show() {
printf "%s environment variables\n" "$ME"
local var val
for var in $VAR_LIST; do
eval val=\$$var
printf "%20s: %s\n" "$var" "$val"
done
}
#------------------------------------------------------------------------------
# Only try to make a directory if it doesn't already exist. This is slightly
# convenient and it helps make or verbose reporting cleaner.
#------------------------------------------------------------------------------
my_mkdir() {
local dir=$1
test -e "$dir" || pretend mkdir -p "$dir"
}
#------------------------------------------------------------------------------
# Only run the command if not PRETEND. Show command if PRETEND or VERBOSE.
#------------------------------------------------------------------------------
pretend() {
[ "$PRETEND" -o "$VERBOSE" ] && echo "$@"
[ "$PRETEND" ] && return
"$@"
}
#-------------------------------------------------------------------------------
# Send "$@". Expects
#
# SHORT_STACK variable, list of single chars that stack
# fatal(msg) routine, fatal("error message")
# takes_param(arg) routine, true if arg takes a value
# eval_argument(arg, [val]) routine, do whatever you want with $arg and $val
#
# Sets "global" variable SHIFT to the number of arguments that have been read.
#-------------------------------------------------------------------------------
read_params() {
# Most of this code is boiler-plate for parsing cmdline args
SHIFT=0
# These are the single-char options that can stack
local arg val
# Loop through the cmdline args
while [ $# -gt 0 -a ${#1} -gt 0 -a -z "${1##-*}" ]; do
arg=${1#-}
shift
SHIFT=$((SHIFT + 1))
# Expand stacked single-char arguments
case $arg in
[$SHORT_STACK][$SHORT_STACK]*)
if echo "$arg" | grep -q "^[$SHORT_STACK]\+$"; then
local old_cnt=$#
set -- $(echo $arg | sed -r 's/([a-zA-Z])/ -\1 /g') "$@"
SHIFT=$((SHIFT - $# + old_cnt))
continue
fi;;
esac
# Deal with all options that take a parameter
if takes_param "$arg"; then
[ $# -lt 1 ] && fatal "Expected a parameter after: -$arg"
val=$1
[ -n "$val" -a -z "${val##-*}" ] \
&& fatal "Suspicious argument after -$arg: $val"
SHIFT=$((SHIFT + 1))
shift
else
case $arg in
*=*) val=${arg#*=} ;;
*) val="???" ;;
esac
fi
eval_argument "$arg" "$val"
done
}
vsay() {
[ "$VERBOSE" ] || return
local fmt=$1 ; shift
printf "$fmt\n" "$@"
}
say() {
local fmt=$1 ; shift
printf "$fmt\n" "$@"
}
qsay() {
[ "$QUIET" ] && return
local fmt=$1 ; shift
printf "$fmt\n" "$@"
}
warn() {
local fmt=$1; shift
printf "$ME$err_co warning:$warn_co $fmt$nc_co\n" "$@" >&2
}
warn_err() {
if [ "$FORCE" ]; then
warn "$@"
else
fatal "$@"
fi
}
fatal() {
local fmt=$1; shift
printf "$ME$err_co fatal error:$warn_co $fmt$nc_co\n" "$@" >&2
exit 2
}
set_colors() {
local e=$(printf "\e")
black="$e[0;30m" ; blue="$e[0;34m" ; green="$e[0;32m" ; cyan="$e[0;36m" ;
red="$e[0;31m" ; purple="$e[0;35m" ; brown="$e[0;33m" ; lt_gray="$e[0;37m" ;
dk_gray="$e[1;30m" ; lt_blue="$e[1;34m" ; lt_green="$e[1;32m" ; lt_cyan="$e[1;36m" ;
lt_red="$e[1;31m" ; magenta="$e[1;35m" ; yellow="$e[1;33m" ; white="$e[1;37m" ;
nc_co="$e[0m" ; brown="$e[0;33m" ;
m_co=$cyan
hi_co=$white
err_co=$red
bold_co=$yellow
warn_co=$yellow
}
main "$@"