Skip to content

Instantly share code, notes, and snippets.

@enten
Last active July 24, 2016 15:19
Show Gist options
  • Save enten/63cedaca9bf019feb71a to your computer and use it in GitHub Desktop.
Save enten/63cedaca9bf019feb71a to your computer and use it in GitHub Desktop.
#!/system/bin/sh
set -u
## ========================================================================================
## alfred : manages an ArchLinux rootfs for enable LXC usage on Android
## ========================================================================================
PROGNAME="alfred"
PROGVERS="0.0.6"
PROGCONF="/data/$PROGNAME"
PROGHELP="Usage: $PROGNAME [cmd]
Setup commands:
install <path> # Install alfred under <path>
uninstall # Uninstall alfred
"
PROGPATH=
PROGTREE=
## ----------------------------------------------------------------------------------------
## Environment
## ----------------------------------------------------------------------------------------
SMOUNT_BIN="/data/cloudlet/smount"
BUSYBOX_BIN="/system/bin/busybox"
BUSYBOX_APPLETS="awk basename chroot grep head rdate tar wget"
NTP_SERVER="ntp.unice.fr"
ROOTFS_IMG_SRC="http://archlinuxarm.org/os/ArchLinuxARM-armv7-latest.tar.gz"
ROOTFS_IMG_DIST="archlinux"
ROOTFS_IMG_RELEASE="latest"
ROOTFS_IMG_ARCH="armv7"
ROOTFS_IMG_NAME=
ROOTFS_IMG_PATH=
AUTO_ACCEPT=
BAD_CONFIG=
READ=
ROOTFS_INIT=$(cat <<EOF
rm -f /etc/resolv.conf
echo "nameserver 8.8.8.8" > /etc/resolv.conf
pacman -Syu --noconfirm
pacman -S --noconfirm git lxc wget
echo "Bye!"
EOF
)
## ----------------------------------------------------------------------------------------
## Main functions
## ----------------------------------------------------------------------------------------
main() {
checkSystem
[ $# -lt 1 ] && notfound
local cmd=$1; shift
case $cmd in
help)
echo "$PROGHELP"
exit
;;
mount|umount)
[ 0 -lt $# ] && notfound
checkInstall
cmd=$cmd"Rootfs"
$cmd
;;
install|uninstall)
[ "$cmd" = "uninstall" ] && checkInstall
$cmd $@
;;
*)
checkInstall
run $cmd $@
;;
esac
}
install() {
[ $# -lt 1 ] && notfound
[ 2 -lt $# ] && notfound
[ 1 -lt $# ] && [ "$2" = "auto-accept" ] && AUTO_ACCEPT=1
[ "$PROGPATH" != "" ] \
&& error "FAILED! Alfred is already installed under path $PROGPATH" \
&& warn "You must REMOVE IT FIRST with 'alfred uninstall'"
#&& fail "You must REMOVE it with 'alfred uninstall'"
PROGPATH=$1
[ "${PROGPATH:0:1}" != "/" ] && PROGPATH="$(pwd)/$PROGPATH"
echo "Alfred will be INSTALLED under the path $PROGPATH"
confirm "Are you sure? [y/N]"
installWizard
echo "Done!"
}
uninstall() {
[ 1 -lt $# ] && notfound || [ 0 -lt $# ] && [ "$1" = "auto-accept" ] && AUTO_ACCEPT=1
echo "Alfred installation under the path $PROGPATH will be REMOVED"
confirm "Are you sure? [y/N]"
umountRootfs
rm -fr $PROGPATH >/dev/null 2>&1
rm -f $PROGCONF >/dev/null 2>&1
echo "Done!"
}
run() {
[ "$BAD_CONFIG" = "1" ] && fail "FAILED! Check your file configuration $PROGCONF"
mountRootfs
local cmd="/bin/bash"
[ 0 -lt $# ] && cmd="$@"
beforeRunning $cmd
changeRoot "$cmd"
afterRunning $cmd
}
## ----------------------------------------------------------------------------------------
## Support functions
## ----------------------------------------------------------------------------------------
warn() {
echo $@ >&2
}
error() {
echo $@ 1>&2
}
fail() {
[ 0 -lt $# ] && error $@
exit 1
}
notfound() {
error "ERROR: command not found"
echo "--"
echo "$PROGHELP"
fail
}
checkBusybox() {
[ ! -e "/system/bin/adb" ] && return
if ! type $BUSYBOX_BIN >/dev/null 2>&1; then
echo "FAILED! This program needs busybox to work" 1>&2
exit 1
fi
for i in $BUSYBOX_APPLETS; do
eval "alias '$i=$BUSYBOX_BIN $i'"
done
}
checkConfig() {
[ -e $PROGCONF ] && . $PROGCONF
[ "$PROGPATH" != "" ] && [ ! -e $PROGPATH ] && PROGPATH= && BAD_CONFIG=1
for i in $PROGTREE; do
[ ! -e "$PROGPATH/$i" ] && BAD_CONFIG=1 && break
done
[ "$BAD_CONFIG" = "1" ] && warn "WARN! Verify your installation under $PROGPATH"
}
checkSystem() {
checkBusybox
checkConfig
[ $(date +%Y) -lt 2000 ] && rdate -s $NTP_SERVER
[ ! -e "/etc/resolv.conf" ] && echo "nameserver 8.8.8.8" > /etc/resolv.conf
}
checkInstall() {
[ "$PROGPATH" = "" ] && fail "FAILED! Alfred is not installed yet"
}
prompt() {
[ 0 -lt $# ] && echo -n "$@ "
read PROMPT
}
confirm() {
[ "$AUTO_ACCEPT" = "1" ] && return
prompt $@
case $PROMPT in
[yY][eE][sS]|[yY])
PROMPT=1
;;
*)
fail "Cancelled."
;;
esac
}
downloadFile() {
if [ ! -e "$2" ]; then
if ! wget -q $1 -O $2; then
rm -f $2 >/dev/null 2>&1
return 1
fi
[ ! -e "$2" ] && return 1
fi
return 0
}
unpackArchive() {
if ! tar --numeric-owner -xf $1 -C $2 >/dev/null 2>&1; then
return 1
fi
return 0
}
writeConfig() {
echo "$1=\"$2\"" >> $PROGCONF
}
updateConfig() {
[ -e "$PROGCONF" ] && rm -f $PROGCONF >/dev/null 2>&1
touch $PROGCONF >/dev/null 2>&1
writeConfig "PROGPATH" $PROGPATH
writeConfig "PROGTREE" "$PROGTREE"
}
installWizard() {
echo -n "[1/5] Creating the install directory... "
if ! mkdir -p $PROGPATH >/dev/null 2>&1; then
fail "KO"
fi
echo "ok"
echo -n "[2/5] Donwloading the rootfs image... "
ROOTFS_IMG_NAME="$ROOTFS_IMG_DIST-$ROOTFS_IMG_RELEASE-$ROOTFS_IMG_ARCH-rootfs.tar.gz"
ROOTFS_IMG_PATH="$PROGPATH/$ROOTFS_IMG_NAME"
if ! downloadFile $ROOTFS_IMG_SRC $ROOTFS_IMG_PATH; then
fail "KO"
fi
echo "ok"
echo -n "[3/5] Unpacking the rootfs image... "
if ! unpackArchive $ROOTFS_IMG_PATH $PROGPATH; then
fail "KO"
fi
PROGTREE=
for i in $(find $PROGPATH -maxdepth 1 -type d); do
i=$(basename $i)
[ "$i" = "$(basename $PROGPATH)" ] && continue
[ -z "$PROGTREE" ] && PROGTREE=$i && continue
PROGTREE="$PROGTREE $i"
done
if ! updateConfig >/dev/null 2>&1; then
fail "KO"
fi
echo "ok"
echo -n "[4/5] Configuring the rootfs... "
echo "$ROOTFS_INIT" > $PROGPATH/rootfs.init
chmod 755 $PROGPATH/rootfs.init >/dev/null 2>&1
if ! run "/rootfs.init" >/dev/null 2>&1; then
fail "KO"
fi
rm -f $PROGPATH/rootfs.init >/dev/null 2>&1
echo "ok"
echo -n "[5/5] Finalizing the installation... "
rm -f $ROOTFS_IMG_PATH >/dev/null 2>&1
echo "ok"
}
execCmd() {
$@ >/dev/null 2>&1
}
mountRootfs() {
execCmd $SMOUNT_BIN / rprivate
if ! grep -q " $PROGPATH " /proc/mounts; then
execCmd mount -o bind $PROGPATH $PROGPATH
fi
for i in acct dev dev/cpuctl dev/pts proc sys sys/fs/cgroup system; do
[ ! -d /$i ] && continue
if grep -q " $PROGPATH/$i " /proc/mounts; then
continue
fi
execCmd mkdir -p $PROGPATH/$i
execCmd mount -o bind /$i $PROGPATH/$i
done
for i in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
if ! grep -q " $PROGPATH/$i " /proc/mounts; then
execCmd mkdir -p $PROGPATH/$i
execCmd $BUSYBOX_BIN mount -n -t cgroup -o $i cgroup $PROGPATH/$i
fi
done
}
umountRootfs() {
cgmanagerStop
for i in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
execCmd umount $PROGPATH/$i
done
for i in system sys/fs/cgroup sys proc dev/pts dev/cpuctl dev acct; do
if ! grep -q " $PROGPATH/$i " /proc/mounts; then
continue
fi
execCmd umount $PROGPATH/$i
done
if grep -q " $PROGPATH " /proc/mounts; then
execCmd umount $PROGPATH
fi
}
changeRoot() {
local env="TERM=$TERM SHELL=/bin/bash USER=root USERNAME=root HOME=/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LD_PRELOAD="
local envBackup="TERM=$TERM SHELL=$SHELL USER=$USER HOME=$HOME PATH=$PATH LD_PRELOAD=$LD_PRELOAD"
for i in $env; do export $i; done
$BUSYBOX_BIN chroot $PROGPATH $@
for i in $envBackup; do export $i; done
}
beforeRunning() {
case $1 in
"lxc-start") cmdLxcStartPrevent "$@" ;;
esac
}
afterRunning() {
case $1 in
"lxc-create") cmdLxcCreateCallback "$@" ;;
esac
}
cgmanagerIsRunning() {
if [ $(changeRoot "ps -x" | grep -c cgmanager) -lt 2 ]; then
return 1
fi
}
cgmanagerStart() {
if ! cgmanagerIsRunning >/dev/null 2>&1; then
changeRoot cgmanager --daemon
fi
}
cgmanagerStop() {
if cgmanagerIsRunning >/dev/null 2>&1; then
changeRoot kill $(changeRoot ps -x | grep cgmanager | head -n1 | awk '{print $1}')
fi
}
cmdLxcStartPrevent() {
cgmanagerStart
}
cmdLxcCreateCallback() {
local nameFound=
local containerName=
local containerPath=
for i in "$@"; do
[ "$nameFound" = "1" ] && containerName=$i && break
[ "$i" = "-n" ] && nameFound=1
done
[ -z "$containerName" ] && return
containerPath=/var/lib/lxc/$containerName/rootfs
[ ! -d "$PROGPATH/$containerPath" ] && return
changeRoot chroot $containerPath passwd
}
## ----------------------------------------------------------------------------------------
## One main to rule them all...
## ----------------------------------------------------------------------------------------
main $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment