Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # mer-sdk-chroot
- # TODO
- #
- # Support a Mer clean setup (ie no .oscrc)
- # Support multiple shells (ie split setup/entry)
- usage()
- {
- cat <<EOF
- usage: $0 [-u <user>] [-m <all|none|root|home>] [-r <SDK root path>] [<command> <args> ..]
- $0 -h
- This is the Mer chroot SDK.
- For information see http://wiki.merproject.org/wiki/Platform_SDK
- If command is not present,
- used to enter the SDK and begin working. The SDK bash shell is a
- login shell. See below for .profile handling
- If command is present,
- used to execute an arbitrary command from within the SDK chroot
- environment. The environment variable MERSDK is set to allow
- SDK detection.
- Options:
- -u System user to link into SDK (not needed if using sudo)
- -m Devices to bind mount from host: none, all (default)
- root, home
- -r The root of the SDK to use - normally derived from the
- pathname of $0
- -h Show this help
- Profile
- Entering the SDK runs the user's normal .profile and any (SDK)
- system profile entries. It will not execute the host's system
- profile entries.
- The environment variable MERSDK is set to allow .profile to
- detect the SDK.
- If the user has a ~/.mersdk.profile then it is sourced after the
- normal .profile handling (this allows the common use case of
- setting a profile to be handled).
- Hooks
- If the user specified has a .mersdkrc in their $HOME, it will be
- sourced to allow hook functions to be defined. Hooks are run as
- root. No commands should be executed immediately.
- These hooks are usually used to define symbolic links from any
- /parentroot/data type filesystems into the SDK root to setup
- system specific shared caches or filesystem layouts etc
- EOF
- return 0
- }
- MY_SSH_AUTH_SOCK=${SSH_AUTH_SOCK#/parentroot}
- [[ $MY_SSH_AUTH_SOCK ]] && MY_SSH_AUTH_SOCK="/parentroot$MY_SSH_AUTH_SOCK"
- if [[ $EUID -ne 0 ]]; then
- exec sudo SSH_AGENT_PID=${SSH_AGENT_PID:-} SSH_AUTH_SOCK=${MY_SSH_AUTH_SOCK} $0 "$@"
- echo "$0 must be run as root and sudo failed; exiting"
- exit 1
- fi
- if cmp -s /proc/$PPID/mountinfo /proc/self/mountinfo; then
- exec unshare -m -- "$0" "$@"
- echo "$0 must be run in private namespace and unshare failed; exiting"
- exit 1
- fi
- # Make sure that mountpoints in the new namespace are really unshared from the
- # parental namespace. See unshare(1).
- # This prevents mounts in the sdk from appearing in the parent fs.
- mount --make-rslave /
- # Use the SUDO value if present
- user=$SUDO_USER || true;
- bind_mount_root="yes";
- bind_mount_home="yes";
- while getopts "u:m:r:" opt; do
- case $opt in
- u ) user=$OPTARG;;
- m )
- case $OPTARG in
- all) ;;
- home)
- bind_mount_root="no";;
- root)
- bind_mount_home="no";;
- none)
- bind_mount_root="no";
- bind_mount_home="no";;
- *) echo "Only 'none', 'all' or 'home' are permitted for -m"
- usage
- exit 1;;
- esac ;;
- r ) sdkroot=$OPTARG;;
- h|\? ) usage
- exit 1;;
- : ) echo "Option -$OPTARG requires an argument." >&2
- usage
- exit 1;;
- * ) usage
- exit 1;;
- esac
- done
- shift $(($OPTIND - 1))
- if [[ -z "${sdkroot}" ]] ; then
- sdkroot=$(dirname $(readlink -f $0))
- else
- sdkroot=$(readlink -f $sdkroot)
- fi
- if [[ ! -f ${sdkroot}/etc/MerSDK ]] ; then
- echo "${sdkroot} does not look like a Mer SDK rootfs"
- echo "if you are sure it is, you may mark it by running"
- echo "echo 'MerSDK' | sudo tee ${sdkroot}/etc/MerSDK"
- exit 1
- fi
- if ! [[ $(basename $(dirname $sdkroot)) == "sdks" ]]; then
- echo "Non-standard SDK installation layout - cannot determine location for "
- echo "SDK targets and toolings. Expected layout is:"
- echo ""
- echo " <path/to/SDKs...>/"
- echo " ├── sdks/"
- echo " │ ├── <sdk_1>/"
- echo " │ ├── <sdk_2>/"
- echo " │ └── .../"
- echo " ├── targets/"
- echo " │ ├── <targets_1>/"
- echo " │ ├── <targets_2>/"
- echo " │ └── .../"
- echo " └── toolings/"
- echo " ├── <tooling_1>/"
- echo " ├── <tooling_2>/"
- echo " └── .../"
- exit 1
- fi
- targetsdir=$(readlink -f ${sdkroot}/../../targets)
- if [[ ! -d ${targetsdir} ]] ; then
- echo "SDK targets location '$targetsdir' does not exist - about to create it."
- read -p "Continue, abort? [c/a] (c)" reply
- if ! [[ $reply =~ ^[cC]?$ ]]; then
- echo "User aborted"
- exit 1
- fi
- mkdir "$targetsdir" || exit
- fi
- toolingsdir=$(readlink -f ${sdkroot}/../../toolings)
- if [[ ! -d ${toolingsdir} ]] ; then
- echo "SDK toolings location '$toolingsdir' does not exist - about to create it."
- read -p "Continue, abort? [c/a] (c)" reply
- if ! [[ $reply =~ ^[cC]?$ ]]; then
- echo "User aborted"
- exit 1
- fi
- mkdir "$toolingsdir" || exit
- fi
- if [[ -z $user ]] ; then
- echo "$0 expects to be run as root using sudo"
- echo "User could not be obtained from \$SUDO_USER, if running as root,"
- echo "please use -u <user>"
- echo
- usage
- exit 1
- fi
- # From now on, exit if variables not set
- set -u
- # Make sure normal users can use any dirs we make
- umask 022
- ################################################################
- # Mount
- mount_bind() {
- if [[ ! -d ${sdkroot}$1 ]]; then
- echo "Directory $1 is missing in SDK root - please report this bug"
- mkdir -p ${sdkroot}$1
- fi
- mount --bind $1 ${sdkroot}$1
- }
- prepare_mountpoints() {
- # This prevents the following mount_bind to hang first time after reboot.
- # Bug in binfmt_misc pseudo-filesystem?
- ls /proc/sys/fs/binfmt_misc -a > /dev/null
- echo "Mounting system directories..."
- mount_bind /proc
- mount_bind /proc/sys/fs/binfmt_misc
- mount_bind /sys
- mount_bind /dev
- mount_bind /dev/pts
- mount_bind /dev/shm
- mount_bind /var/lib/dbus
- mount_bind /var/run/dbus
- echo "Mounting $targetsdir as /srv/mer/targets"
- mkdir -p ${sdkroot}/srv/mer/targets
- mount --rbind ${targetsdir} ${sdkroot}/srv/mer/targets/
- echo "Mounting $toolingsdir as /srv/mer/toolings"
- mkdir -p ${sdkroot}/srv/mer/toolings
- mount --rbind ${toolingsdir} ${sdkroot}/srv/mer/toolings/
- if [[ $bind_mount_root == "yes" ]] ; then
- echo "Mounting / as /parentroot"
- mkdir -p ${sdkroot}/parentroot
- mount --rbind / ${sdkroot}/parentroot/
- fi
- mkdir -p ${sdkroot}/lib/modules/`uname -r`
- mount_bind /lib/modules/`uname -r`
- }
- # Replace 'shell' field in `getent passwd` output with /bin/bash if the shell
- # is not available in the chroot
- fix_shell() {
- local shell= other=
- while IFS=: read shell other; do
- if ! [[ -e ${sdkroot}${shell} ]]; then
- shell=/bin/bash
- fi
- echo "$other:$shell"
- done <<<"$(awk -F: -v OFS=: '{print $7,$1,$2,$3,$4,$5,$6}')"
- }
- prepare_user() {
- # remove mer user if present
- sed -i -e "/^mer:/d" ${sdkroot}/etc/passwd
- # getent is probably best for user data
- sed -i -e "/^${user}:/d" ${sdkroot}/etc/passwd
- getent passwd $user |fix_shell >> ${sdkroot}/etc/passwd
- group=$(getent passwd $user | cut -f4 -d:)
- sed -i -e "/^[^:]*:[^:]*:${group}:/d" ${sdkroot}/etc/group
- getent group $group >> ${sdkroot}/etc/group
- HOMEDIR=$(getent passwd $user | cut -f6 -d:)
- install --owner $user --group $(id -gn $user) -d ${sdkroot}${HOMEDIR}
- if [[ $bind_mount_home == "yes" ]] ; then
- echo "Mounting home directory: ${HOMEDIR}"
- mount --bind ${HOMEDIR} ${sdkroot}${HOMEDIR}
- # Now the sdk uses a private namespace, there's no need to
- # make it unbindable
- mount --make-shared ${sdkroot}${HOMEDIR}
- fi
- echo "$user ALL=NOPASSWD: ALL" > ${sdkroot}/etc/sudoers.d/$user
- chmod 0440 ${sdkroot}/etc/sudoers.d/$user
- }
- prepare_etc() {
- # Symlink to parentroot to support dynamic resolv.conf on host
- rm -f ${sdkroot}/etc/resolv.conf
- resolv=$(readlink -fn /etc/resolv.conf) # some systems use symlinks to /var/run/...
- ln -s /parentroot/$resolv ${sdkroot}/etc/resolv.conf
- # Fixup old SDKs with broken /etc/mtab since this won't be fixed
- # by any package updates
- if [[ ! -L ${sdkroot}/etc/mtab ]]; then
- echo "The /etc/mtab file in the SDK is not a symbolic link - forcing it to link to /proc/self/mounts to fix https://bugs.merproject.org/show_bug.cgi?id=385"
- rm -f ${sdkroot}/etc/mtab
- ln -s /proc/self/mounts ${sdkroot}/etc/mtab
- fi
- }
- ################
- setup_user_hooks(){
- # Access any user hooks
- [[ -e $HOMEDIR/.mersdkrc ]] && . $HOMEDIR/.mersdkrc
- }
- run_user_hook() {
- hook=$1
- [[ $(type -t $hook) == "function" ]] && {
- echo "User hook $hook"
- $hook
- }
- }
- ################
- ################################################################
- retval=0
- cwd=$(pwd)
- # For back compatibility abort on 'mount/umount' and warn on 'enter'
- if [[ $# == 1 ]]; then
- case $1 in
- mount|umount )
- cat <<EOF
- ERROR: the mer-sdk-chroot command no longer needs or supports 'mount/umount/enter'
- Just enter the SDK using:
- $0
- SDK mount/umount is handled automatically and safely.
- EOF
- exit 1;;
- enter )
- cat <<EOF
- WARNING: sdk 'enter' is deprecated. Just enter the SDK using:
- $0
- Entering the SDK as requested
- EOF
- shift;;
- esac
- fi
- prepare_mountpoints # host / and data and /proc and similar
- prepare_user # in /etc/passwd
- setup_user_hooks # (after prepare so HOMEDIR is known)
- prepare_etc # resolv.conf and ssl certs
- run_user_hook mount_sdk
- run_user_hook enter_sdk
- trap 'rv=$?; run_user_hook leave_sdk; exit "$rv"' EXIT
- if [[ ${1:-} == 'exec' ]]; then
- cat <<EOF
- WARN: sdk 'exec' is deprecated. Just execute SDK commands using:
- $0 <cmd> <args>
- Executing commands as requested
- EOF
- shift # Remove the offending 'exec'
- if ! [[ $1 ]]; then
- echo "You must supply a command to exec"
- usage
- exit 1
- fi
- fi
- if [[ $# -gt 0 ]]; then
- cmd=("$@")
- else
- cmd=(bash --init-file /mer-bash-setup -i)
- echo "Entering chroot as $user"
- fi
- setarch i386 chroot ${sdkroot} /bin/su -s /bin/bash -l $user -- -c "$(cat <<END
- [[ -d "$cwd" ]] && cd "$cwd"
- export SSH_AUTH_SOCK='${MY_SSH_AUTH_SOCK}'
- export SSH_AGENT_PID=${SSH_AGENT_PID:-}
- export MERSDK='$sdkroot'
- exec $(printf "%q " "${cmd[@]}")
- END
- )"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement