Current File : //usr/local/jetapps/usr/share/rear/lib/linux-functions.sh |
# linux-functions.sh
#
# linux functions for Relax-and-Recover
#
# This file is part of Relax-and-Recover, licensed under the GNU General
# Public License. Refer to the included COPYING for full text of license.
# The way how we use bash with lots of (nested) functions and read etc. seems to trigger a bash
# bug that causes leaked file descriptors. lvm likes to complain about that but since we
# cannot fix the bash we suppress these lvm warnings, see
# http://osdir.com/ml/bug-bash-gnu/2010-04/msg00080.html
# http://stackoverflow.com/questions/2649240/bash-file-descriptor-leak
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=466138
export LVM_SUPPRESS_FD_WARNINGS=1
# check if udev is available in a sufficiently recent version
# has_binary succeeds when one of its arguments exists
# newer systems (e.g. SLES11) have udevadm
# older systems (e.g. SLES10 or RHEL 4) have udevtrigger udevsettle udevinfo or udevstart
function have_udev () {
test -d /etc/udev/rules.d && has_binary udevadm udevtrigger udevsettle udevinfo udevstart && return 0
return 1
}
# try calling 'udevadm trigger' or 'udevtrigger' or fallback
# but waiting for udev and "kicking udev" both miss the point
# see https://github.com/rear/rear/issues/791
function my_udevtrigger () {
# first try the most current way, newer systems (e.g. SLES11) have 'udevadm trigger'
has_binary udevadm && udevadm trigger "$@" && return 0
# then try an older way, older systems (e.g. SLES10) have 'udevtrigger'
has_binary udevtrigger && udevtrigger "$@" && return 0
# as first fallback do what start_udev does on RHEL 4
if has_binary udevstart ; then
local udevd_pid=$( pidof -x udevd )
test "$udevd_pid" && kill $udevd_pid
udevstart </dev/null &>/dev/null && return 0
fi
# as final fallback just wait a bit and hope for the best
sleep 10
}
# try calling 'udevadm settle' or 'udevsettle' or fallback
# but waiting for udev and "kicking udev" both miss the point
# see https://github.com/rear/rear/issues/791
function my_udevsettle () {
# first try the most current way, newer systems (e.g. SLES11) have 'udevadm settle'
has_binary udevadm && udevadm settle "$@" && return 0
# then try an older way, older systems (e.g. SLES10) have 'udevsettle'
has_binary udevsettle && udevsettle "$@" && return 0
# as first fallback re-implement udevsettle for older systems
if [ -e /sys/kernel/uevent_seqnum ] && [ -e /dev/.udev/uevent_seqnum ] ; then
local tries=0
while [ "$( cat /sys/kernel/uevent_seqnum )" = "$( cat /dev/.udev/uevent_seqnum )" ] && [ "$tries" -lt 10 ] ; do
sleep 1
let tries=tries+1
done
return 0
fi
# as final fallback just wait a bit and hope for the best
sleep 10
}
# call 'udevadm info' or 'udevinfo'
function my_udevinfo () {
# first try the most current way, newer systems (e.g. SLES11) have 'udevadm info'
if has_binary udevadm ; then
udevadm info "$@"
return 0
fi
# then try an older way, older systems (e.g. SLES10) have 'udevinfo'
if has_binary udevinfo ; then
udevinfo "$@"
return 0
fi
# no fallback
return 1
}
# find out which are the storage drivers to use on this system
# returns a list of storage drivers on STDOUT
# optionally $1 specifies the directory where to search for
# drivers files
function FindStorageDrivers () {
# The special user setting MODULES=( 'no_modules' ) enforces that
# no kernel modules get included in the rescue/recovery system
# regardless of what modules are currently loaded.
# Test the first MODULES array element because other scripts
# in particular rescue/GNU/Linux/240_kernel_modules.sh
# already appended other modules to the MODULES array:
test "no_modules" = "$MODULES" && return
if (( ${#STORAGE_DRIVERS[@]} == 0 )) ; then
if ! grep -E 'kernel/drivers/(block|firewire|ide|ata|md|message|scsi|usb/storage)' /lib/modules/$KERNEL_VERSION/modules.builtin ; then
Error "FindStorageDrivers called but STORAGE_DRIVERS is empty and no builtin storage modules found"
fi
fi
{
while read module junk ; do
IsInArray "$module" "${STORAGE_DRIVERS[@]}" && echo $module
done < <(lsmod)
find ${1:-$VAR_DIR/recovery} -name storage_drivers -exec cat '{}' \; 2>/dev/null
} | grep -v -E '(loop)' | sort -u
# blacklist some more stuff here that came in the way on some systems
return 0
# always return 0 as the grep return code is meaningless
}
# Determine all required shared objects (shared/dynamic libraries)
# for programs and/or shared objects (binaries) specified in $@.
# RequiredSharedObjects outputs the set of required shared objects on STDOUT.
# The output are absolute paths to the required shared objects.
# The output can also be symbolic links (also as absolute paths).
# In case of symbolic links only the link but not the link target is output.
function RequiredSharedObjects () {
has_binary ldd || Error "Cannot run RequiredSharedObjects() because there is no ldd binary"
Log "RequiredSharedObjects: Determining required shared objects"
# It uses 'ldd' to determine all required shared objects because 'ldd' outputs
# also transitively required shared objects i.e. libraries needed by libraries,
# e.g. for /usr/sbin/parted also the libraries needed by the libparted library:
# # ldd /usr/sbin/parted
# linux-vdso.so.1 (0x00007ffd68fe1000)
# libparted.so.2 => /usr/lib64/libparted.so.2 (0x00007f0c72bee000)
# libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f0c729c4000)
# libreadline.so.6 => /lib64/libreadline.so.6 (0x00007f0c72778000)
# libc.so.6 => /lib64/libc.so.6 (0x00007f0c723d5000)
# libuuid.so.1 => /usr/lib64/libuuid.so.1 (0x00007f0c721d0000)
# libdevmapper.so.1.02 => /lib64/libdevmapper.so.1.02 (0x00007f0c71f85000)
# libblkid.so.1 => /usr/lib64/libblkid.so.1 (0x00007f0c71d43000)
# libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f0c71b0f000)
# /lib64/ld-linux-x86-64.so.2 (0x000055eff2882000)
# libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f0c718e8000)
# libudev.so.1 => /usr/lib64/libudev.so.1 (0x00007f0c716c8000)
# libpthread.so.0 => /lib64/noelision/libpthread.so.0 (0x00007f0c714ab000)
# libpcre.so.1 => /usr/lib64/libpcre.so.1 (0x00007f0c71244000)
# libdl.so.2 => /lib64/libdl.so.2 (0x00007f0c71040000)
# libcap.so.2 => /lib64/libcap.so.2 (0x00007f0c70e3b000)
# librt.so.1 => /lib64/librt.so.1 (0x00007f0c70c32000)
# libm.so.6 => /lib64/libm.so.6 (0x00007f0c70935000)
# libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f0c7071e000)
# # file /usr/lib64/libparted.so.2
# /usr/lib64/libparted.so.2: symbolic link to `libparted.so.2.0.0'
# # mv /usr/lib64/libparted.so.2.0.0 /usr/lib64/libparted.so.2.0.0.away
# # ldd /usr/sbin/parted /usr/lib64/libparted.so.2.0.0.away
# /usr/sbin/parted:
# linux-vdso.so.1 (0x00007ffc38505000)
# libparted.so.2 => not found
# libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007fe0f4b5e000)
# libreadline.so.6 => /lib64/libreadline.so.6 (0x00007fe0f4913000)
# libc.so.6 => /lib64/libc.so.6 (0x00007fe0f4570000)
# libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007fe0f433b000)
# /lib64/ld-linux-x86-64.so.2 (0x000055e2549e2000)
# /usr/lib64/libparted.so.2.0.0.away:
# linux-vdso.so.1 (0x00007fffdbb8f000)
# libuuid.so.1 => /usr/lib64/libuuid.so.1 (0x00007f3c9a87d000)
# libdevmapper.so.1.02 => /lib64/libdevmapper.so.1.02 (0x00007f3c9a633000)
# libblkid.so.1 => /usr/lib64/libblkid.so.1 (0x00007f3c9a3f0000)
# libc.so.6 => /lib64/libc.so.6 (0x00007f3c9a04d000)
# /lib64/ld-linux-x86-64.so.2 (0x0000563ffc5f1000)
# libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f3c99e27000)
# libudev.so.1 => /usr/lib64/libudev.so.1 (0x00007f3c99c06000)
# libpthread.so.0 => /lib64/noelision/libpthread.so.0 (0x00007f3c999e9000)
# libpcre.so.1 => /usr/lib64/libpcre.so.1 (0x00007f3c99783000)
# libdl.so.2 => /lib64/libdl.so.2 (0x00007f3c9957e000)
# libcap.so.2 => /lib64/libcap.so.2 (0x00007f3c99379000)
# librt.so.1 => /lib64/librt.so.1 (0x00007f3c99171000)
# libm.so.6 => /lib64/libm.so.6 (0x00007f3c98e73000)
# libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f3c98c5c000)
# The 'ldd' output (when providing more than one argument) has 5 cases.
# So we have to distinguish lines of the following form (indentation is done with tab '\t'):
# 1. Line: "/path/to/binary:" -> current file argument for ldd
# 2. Line: " lib (mem-addr)" -> virtual library
# 3. Line: " lib => not found" -> print error to stderr
# 4. Line: " lib => /path/to/lib (mem-addr)" -> print $3 '/path/to/lib'
# 5. Line: " /path/to/lib => /path/to/lib2 (mem-addr)" -> print $3 '/path/to/lib2'
# 6. Line: " /path/to/lib (mem-addr)" -> print $1 '/path/to/lib'
local file_for_ldd=""
local file_owner_name=""
# It is crucial to append to /dev/$DISPENSABLE_OUTPUT_DEV (cf. 'Print' in lib/_input-output-functions.sh):
for file_for_ldd in "$@" ; do
# Skip non-regular files like directories, device files, and non-existent files
# cf. similar code in build/GNU/Linux/100_copy_as_is.sh
# but here symbolic links must not be skipped (e.g. /sbin/mkfs.ext2 -> /usr/sbin/mkfs.ext2)
# otherwise there would be binaries in the recovery system without required libraries:
test -f "$file_for_ldd" || continue
# Skip the ldd test for kernel modules and firmware files
# which could happen via COPY_AS_IS+=( /lib/firmware/my_hardware )
# cf. the code in build/default/990_verify_rootfs.sh
grep -Eq '/lib/modules/|/lib.*/firmware/' <<<"$file_for_ldd" && continue
# Skip files that are not owned by a trusted user to mitigate possible ldd security issues
# because some versions of ldd may directly execute the file (see "man ldd")
# which could lead to the execution of arbitrary programs as user 'root'
# in particular when directories are specified in COPY_AS_IS that may contain
# unexpected files like programs from arbitrary (possibly untrusted) users
# like COPY_AS_IS+=( /home/JohnDoe ) when JohnDoe is not a trusted user.
if test "$TRUSTED_FILE_OWNERS" ; then
file_owner_name="$( stat -c %U $file_for_ldd )"
if ! IsInArray "$file_owner_name" "${TRUSTED_FILE_OWNERS[@]}" ; then
Log "RequiredSharedObjects: Skipping 'ldd' for '$file_for_ldd' (owner '$file_owner_name' not in TRUSTED_FILE_OWNERS)"
continue
fi
fi
ldd $file_for_ldd
# It is crucial to filter the output of all those ldd calls in the 'for' loop
# through one "awk ... | sort -u" pipe to output the set of required shared objects
# (a mathematical set does not contain duplicate elements):
done 2>>/dev/$DISPENSABLE_OUTPUT_DEV | awk ' /^\t.+ => not found/ { print "Shared object " $1 " not found" > "/dev/stderr" }
/^\t.+ => \// { print $3 }
/^\t\// && !/ => / { print $1 } ' | sort -u
}
# Provide a shell, with custom exit-prompt and history
function rear_shell () {
local prompt=$1
local history=$2
# Set fallback exit prompt:
test "$prompt" || prompt="Are you sure you want to exit the Relax-and-Recover shell ?"
# Set some history:
local histfile="$TMP_DIR/.bash_history"
echo "exit" >$histfile
test "$history" && echo -e "exit\n$history" >$histfile
# Setup .bashrc:
local bashrc="$TMP_DIR/.bashrc"
cat <<EOF >$bashrc
export PS1="rear> "
ask_exit() {
read -p "$prompt " REPLY
if [[ "\$REPLY" =~ ^[Yy1] ]] ; then
\exit
fi
}
rear() {
echo "ERROR: You cannot run rear from within the Relax-and-Recover shell !" >&2
}
alias exit=ask_exit
alias halt=ask_exit
alias poweroff=ask_exit
alias reboot=ask_exit
alias shutdown=ask_exit
cd $VAR_DIR
EOF
# Run 'bash' with the original STDIN STDOUT and STDERR when 'rear' was launched by the user
# to get input from the user and to show output to the user (cf. _input-output-functions.sh):
HISTFILE="$histfile" bash --noprofile --rcfile $bashrc 0<&6 1>&7 2>&8
}
# Return the filesystem name related to a path
function filesystem_name () {
local path=$1
local fs=$(df -Pl "$path" | awk 'END { print $6 }')
if [[ -z "$fs" ]]; then
echo "/"
else
echo "$fs"
fi
}