JuNest is a script able to install a minimal Arch Linux system on a partition and also able to use a "nested system" on the main OS (EasyOS in the present case)
So the idea is to use this script with a other system than Arch Linux.
I tested with Devuan (a fork of Debian without systemd as init)
According to https://wiki.debian.org/Debootstrap :
debootstrap is a tool which will install a Debian base system into a subdirectory of another, already installed system. It doesn't require an installation CD, just access to a Debian repository.
Debootstraps works also with Devuan and Ubuntu.
Debootstrap is available as a .deb package from a Debian repository or from a Devuan repository (and probably from many other sources)
It is impossible to install Devuan with the debootstrap from Debian (In fact if it is launched with a Devuan release as option, it installs Debian!)
The debootstrap from Devuan allows to install Devuan and also Debian.
Debootstrap .deb from Devuan converted in pet :
Below here is a big script, named nest and derived from junest, that installs and uses the release daedalus of Devuan in /mnt/wkg/daeadalus
(By default xorg and synaptic are also installed)
nest (to make executable and to place in /usr/bin) :
Code: Select all
#!/usr/bin/bash
#
#Nest is derived from JuNest (https://github.com/fsquillace/junest).
#JuNest was simplified and modified to use Devuan/Debian/Ubuntu instead of Arch Linux (as nested system) and to be usable in EasyOS.
#By default, a minimal Devuan distro is installed.
#
#This script requires the Devuan debootstrap package to work with Devuan, Debian or Ubuntu.
#The Debian debootsrap package is suitable to work with Debian and Ubuntu but not with Devuan
TEXTDOMAIN=nest
export TEXTDOMAIN
. /usr/bin/gettext.sh
# To keep time in sync between the host and the Nest system while traveling to another time zone, uncomment the next line
#copy_file /etc/localtime
# To change the default values, modify these lines :
#Release that will be used. By default, it's daedalus, the stable release of Devuan at time of the creation of this script. To change to Debian, change both RELEASE and MIRROR.
RELEASE=${RELEASE:-daedalus}
# The default mirror for Devuan is http://dev.devuan.org/merged. It had problems when the scipt was tested.
# See https://sledjhamr.org/apt-panopticon/results/Report-web.html for a list of mirrors for Devuan with current status.
# For DEBIAN you can use MIRROR=https://deb.debian.org/debian
MIRROR=https://devuan.packet-gain.de/merged
#Options to pass to debootstrap. For experts. See possible options on the man page https://linux.die.net/man/8/debootstrap
OPTIONS=
# The default Nest directory is at the root of the working partition(/mnt/wkg). So it is not included in the snapshots in EasyOS
NEST_HOME=${NEST_HOME:-/mnt/wkg/$RELEASE}
NEST_TEMPDIR=${NEST_TEMPDIR:-/tmp}
# To not install xorg in the Nest system during the setup change true to false(In this case, Synaptic will not be installed even if INSTALLSYNAPTIC=true)
INSTALLXORG=true
# To not install synaptic in the Nest system during the setup change true to false
INSTALLSYNAPTIC=true
###################################
### Used scripts ###
###################################
# The used scripts in the directory where the executable is installed(=$JUNEST_BASE) by the real JuNest have been copied here, after simplification and small changes.
# The parts are "utils, common, setup, chroot, namespace, proot and wrappers".
# The setup part was completely rewritten because here the nested system is Debian (or a variant) and not Arch Linux.
### source "${JUNEST_BASE}/lib/utils/utils.sh"
NULL_EXCEPTION=11
WRONG_ANSWER=33
#######################################
# Check if the argument is null.
#
# Globals:
# None
# Arguments:
# argument ($1) : Argument to check.
# Returns:
# 0 : If argument is not null.
# NULL_EXCEPTION : If argument is null.
# Output:
# None
#######################################
function check_not_null() {
[ -z "$1" ] && { error "Error: null argument $1"; return $NULL_EXCEPTION; }
return 0
}
#######################################
# Redirect message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function echoerr() {
echo "$@" 1>&2;
}
#######################################
# Print an error message to stderr and exit program.
#
# Globals:
# None
# Arguments:
# msg ($@) : Message to print.
# Returns:
# 1 : The unique exit status printed.
# Output:
# Message printed to stderr.
#######################################
function die() {
error "$@"
exit 1
}
#######################################
# Print an error message to stderr and exit program with a given status.
#
# Globals:
# None
# Arguments:
# status ($1) : The exit status to use.
# msg ($2-) : Message to print.
# Returns:
# $? : The $status exit status.
# Output:
# Message printed to stderr.
#######################################
function die_on_status() {
status=$1
shift
error "$@"
exit "$status"
}
#######################################
# Print an error message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function error() {
echoerr -e "\033[1;31m$*\033[0m"
}
#######################################
# Print a warn message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function warn() {
# $@: msg (mandatory) - str: Message to print
echoerr -e "\033[1;33m$*\033[0m"
}
#######################################
# Print an info message to stdout.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stdout.
#######################################
function info(){
echo -e "\033[1;36m$*\033[0m"
}
#######################################
# Ask a question and wait to receive an answer from stdin.
# It returns $default_answer if no answer has be received from stdin.
#
# Globals:
# None
# Arguments:
# question ($1) : The question to ask.
# default_answer ($2) : Possible values: 'Y', 'y', 'N', 'n' (default: 'Y')
# Returns:
# 0 : If user replied with either 'Y' or 'y'.
# 1 : If user replied with either 'N' or 'n'.
# WRONG_ANSWER : If `default_answer` is not one of the possible values.
# Output:
# Print the question to ask.
#######################################
function ask(){
local question=$1
local default_answer=$2
check_not_null "$question"
if [ -n "$default_answer" ]
then
local answers="Y y N n"
[[ "$answers" =~ $default_answer ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; }
fi
local default="Y"
[ -z "$default_answer" ] || default=$(echo "$default_answer" | tr '[:lower:]' '[:upper:]')
local other="n"
[ "$default" == "N" ] && other="y"
local prompt
prompt=$(info "$question (${default}/${other})> ")
local res="none"
while [ "$res" != "Y" ] && [ "$res" != "N" ] && [ "$res" != "" ];
do
read -r -p "$prompt" res
res=$(echo "$res" | tr '[:lower:]' '[:upper:]')
done
[ "$res" == "" ] && res="$default"
[ "$res" == "Y" ]
}
function insert_quotes_on_spaces(){
# It inserts quotes between arguments.
# Useful to preserve quotes on command
# to be used inside sh -c/bash -c
local C=""
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
temp_C="\"$i\""
else
temp_C="$i"
fi
# Handle edge case when C is empty to avoid adding an extra space
if [[ -z $C ]]
then
C="$temp_C"
else
C="$C $temp_C"
fi
done
echo "$C"
}
### source "${JUNEST_BASE}/lib/core/common.sh"
# This module contains all common functionalities for Nest.
NAME='Nest'
CMD='nest'
DESCRIPTION=$(gettext 'A minimal Devuan (or Debian or variant) distro that runs upon other Linux distros')
ROOT_ACCESS_ERROR=105
ARCH="x86_64"
LD_LIB="${NEST_HOME}/lib64/ld-linux-x86-64.so.2"
ORIGIN_WD=$(pwd)
################## EXECUTABLES ################
# List of executables that are run inside Nest:
DEFAULT_SH=("/bin/bash" "--login")
# List of executables that are run in the host OS:
BWRAP="${NEST_HOME}/usr/bin/bwrap"
PROOT="${NEST_HOME}/usr/bin/proot-${ARCH}"
LD_EXEC="$LD_LIB --library-path ${NEST_HOME}/usr/lib:${NEST_HOME}/lib"
#LD_EXEC="$LD_LIB --library-path ${NEST_HOME}/usr/lib:${NEST_HOME}/lib:${NEST_HOME}/lib/x86_64-linux-gnu"
function bwrap_cmd(){
#$LD_EXEC "$BWRAP" --dev-bind / / "${DEFAULT_SH[0]}" "-c" ":"
# LD_PRELOAD=$NEST_HOME/lib/x86_64-linux-gnu/libselinux.so.1 $LD_EXEC "$BWRAP" --dev-bind / / "${DEFAULT_SH[0]}" "-c" ":"
# echo "$BWRAP" "${@}"
LD_PRELOAD=$NEST_HOME/lib/x86_64-linux-gnu/libselinux.so.1 $LD_EXEC "$BWRAP" "${@}"
# LD_PRELOAD=/daedalus/lib/x86_64-linux-gnu/libselinux.so.1 /daedalus/lib64/ld-linux-x86-64.so.2 --library-path /daedalus/usr/lib:/daedalus/lib /daedalus/usr/bin/bwrap --bind /daedalus / --bind /root /root --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try /run/user/$(id -u) /run/user/$(id -u) --unshare-user-try /bin/bash --login
# LD_PRELOAD=/daedalus/lib/x86_64-linux-gnu/libselinux.so.1 /daedalus/lib64/ld-linux-x86-64.so.2 --library-path /daedalus/usr/lib:/daedalus/lib /daedalus/usr/bin/bwrap --bind /daedalus / --bind /root /root --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try /run/user/$'id -u' /run/user/$'id -u' --unshare-user-try /bin/bash --login
#LD_PRELOAD=/daedalus/lib/x86_64-linux-gnu/libselinux.so.1 /daedalus/lib64/ld-linux-x86-64.so.2 --library-path /daedalus/usr/lib:/daedalus/lib /daedalus/usr/bin/bwrap --bind /daedalus / --bind /root /root --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try /run/user/0 /run/user/0 --unshare-user-try /bin/bash --login
# $LD_EXEC "$BWRAP" "${@}"
}
function proot_cmd(){
local proot_args="$1"
shift
if ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":"
then
${PROOT} ${proot_args} "${@}"
elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":"
then
warn "Warn: Proot is not properly working. Disabling SECCOMP and expect the application to run slowly in particular when it uses syscalls intensively."
warn "Try to use Linux namespace instead as it is more reliable: junest ns"
PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${@}"
else
die "Error: Something went wrong with proot command. Exiting"
fi
}
############## COMMON FUNCTIONS ###############
#######################################
# Provide the proot common binding options for both normal user and fakeroot.
# The list of bindings can be found in `proot --help`. This function excludes
# /etc/mtab file so that it will not give conflicts with the related
# symlink in the image.
#
# Globals:
# HOME (RO) : The home directory.
# RESULT (WO) : Contains the binding options.
# Arguments:
# None
# Returns:
# None
# Output:
# None
#######################################
function provide_common_bindings(){
RESULT=""
local re='(.*):.*'
for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" "/run/user/$(id -u)"
do
if [[ $bind =~ $re ]]
then
[ -e "${BASH_REMATCH[1]}" ] && RESULT="-b $bind $RESULT"
else
[ -e "$bind" ] && RESULT="-b $bind $RESULT"
fi
done
return 0
}
#######################################
# Build passwd and group files using getent command.
# If getent fails the function fallbacks by copying the content from /etc/passwd
# and /etc/group.
#
# The generated passwd and group will be stored in $NEST_HOME/etc/junest.
#
# Globals:
# NEST_HOME (RO) : The JuNest home directory.
# Arguments:
# None
# Returns:
# None
# Output:
# None
#######################################
# In EasyOS, getent exists so a test in junest was removed here.
# The change of the lists of users and groups in the Nest system by the lists from EasyOS could cause problems
# because some users or groups system could exist or have differents ids in the Nest system and not in the host.
# So this change was removed
# E.g. video is the user 44 in Debian (see https://salsa.debian.org/debian/base-passwd/blob/master/group.master)
function copy_passwd_and_group(){
getent passwd root >> "${NEST_HOME}"/etc/passwd
getent group root >> "${NEST_HOME}"/etc/group
getent passwd spot >> "${NEST_HOME}"/etc/passwd
getent group spot >> "${NEST_HOME}"/etc/group
if [ ! "${USER}" == "root" ] && [ ! "${USER}" == "spot" ]
then
getent passwd "${USER}" >> "${NEST_HOME}"/etc/passwd
getent group "${USER}" >> "${NEST_HOME}"/etc/group
fi
}
function copy_file() {
local file="${1}"
# -f option ensure to remove destination file if it cannot be opened
# https://github.com/fsquillace/junest/issues/284
# In this script the option --remove-destination was added
[[ -r "$file" ]] && cp -f --remove-destination "$file" "${NEST_HOME}/$file"
return 0
}
function copy_common_files() {
copy_file /etc/host.conf
copy_file /etc/hosts
copy_file /etc/nsswitch.conf
copy_file /etc/resolv.conf
return 0
}
### source "${JUNEST_BASE}/lib/core/setup.sh"
# This function tests if $NEST_HOME exists and is not empty. It is not used in this script. So we can resume an infinished installation.
# To use it , uncomment line 1004 and following
function is_env_installed(){
[[ -d "$NEST_HOME" ]] && [[ "$(ls -A "$NEST_HOME")" ]] && return 0
return 1
}
#######################################
# Setup the Nest system.
#######################################
function setup_env(){
command -v debootstrap
if [ $? ]; then
echo "$(gettext 'debootstrap will download a minimal system. It could take a while')"
debootstrap --arch amd64 $OPTIONS $RELEASE $NEST_HOME $MIRROR
if [ ! $? ]
then
echo
echo "$(gettext 'Something went wrong. You shoud try again')"
else
echo
echo "$(gettext 'Download of the debootstrap image finished')"
fi
## The next lines are probably useless if debootstrap worked without error.
#rmdir $NEST_HOME/var/cache/apt/archives/partial
#rmdir $NEST_HOME/var/lib/apt/lists/auxfiles
# The two next lines are a trick to prevent annoying error message.
mkdir -p $NEST_HOME$NEST_HOME/lib/x86_64-linux-gnu/
cp $NEST_HOME/lib/x86_64-linux-gnu/libselinux.so.1 $NEST_HOME$NEST_HOME/lib/x86_64-linux-gnu/libselinux.so.1
copy_file /etc/localtime
mkdir $NEST_HOME/home/spot
mount -t proc /proc $NEST_HOME/proc/
mount -t sysfs /sys $NEST_HOME/sys/
if $INSTALLXORG
then
echo
echo "$(gettext 'Download and installation of xorg')"
chroot $NEST_HOME apt-get -y install xorg
if $INSTALLSYNAPTIC
then
echo
echo "$(gettext 'Download and installation of Synaptic')"
chroot $NEST_HOME apt-get -y install synaptic
fi
fi
echo
echo "$(gettext 'Download and installation of bubblewrap and proot')"
chroot $NEST_HOME apt-get -y install bubblewrap
chroot $NEST_HOME apt-get -y install proot
echo
echo "$(gettext 'Installation and generation of locales')"
chroot $NEST_HOME apt-get -y install locales-all
chroot $NEST_HOME apt-get -y install locales
umount $NEST_HOME/sys
umount $NEST_HOME/proc
touch /etc/profile.locale
grep "$NEST_HOME/usr/bin_wrappers" /etc/profile.locale || ( echo n && echo "PATH=\$PATH:$NEST_HOME/usr/bin_wrappers" >> /etc/profile.locale )
mkdir -p "${NEST_HOME}/usr/bin_wrappers"
else
echo "$(gettext 'debootstrap must be installed to setup the Nest image')"
exit
fi
}
#######################################
# Remove an existing Nest system.
#######################################
function delete_env(){
! ask "Are you sure to delete ${NAME} located in ${NEST_HOME}" "N" && return
if mountpoint -q "${NEST_HOME}"
then
info "There are mounted directories inside ${NEST_HOME}"
if ! umount --force "${NEST_HOME}"
then
error "Cannot umount directories in ${NEST_HOME}"
die "Try to delete ${NAME} using root permissions"
fi
fi
# the CA directories are read only and can be deleted only by changing the mod
chmod -R +w "${NEST_HOME}"/etc/ca-certificates
if rm -rf "${NEST_HOME}"
then
info "${NAME} deleted in ${NEST_HOME}"
else
error "Error: Cannot delete ${NAME} in ${NEST_HOME}"
fi
}
### source "${JUNEST_BASE}/lib/core/chroot.sh"
function _run_env_as_xroot(){
local cmd=$1
local backend_args="$2"
local no_copy_files="$3"
shift 3
local uid=$UID
# SUDO_USER is more reliable compared to SUDO_UID
[[ -z $SUDO_USER ]] || uid=$SUDO_USER:$SUDO_GID
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# With chown the ownership of the files is assigned to the real user
trap - QUIT EXIT ABRT KILL TERM INT
trap "[ -z $uid ] || chown -R ${uid} ${NEST_HOME};" EXIT QUIT ABRT TERM INT
if ! $no_copy_files
then
copy_common_files
fi
$cmd $backend_args "$NEST_HOME" "${DEFAULT_SH[@]}" "${args[@]}"
}
#######################################
# Run Nest as real root via chroot command.
#
# Globals:
# NEST_HOME (RO) : The Nest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# DEFAULT_SH (RO) : Contains the default command to run in Nest.
# Arguments:
# backend_args ($1) : The arguments to pass to backend program
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to Nest environment.
# cmd ($3-?) : The command to run inside Nest environment.
# Default command is defined by DEFAULT_SH variable.
# Output:
# - : The command output.
#######################################
function run_env_as_chroot(){
local backend_command="${1:-chroot}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
_run_env_as_xroot "$backend_command" "$backend_args" "$no_copy_files" "$@"
}
### source "${JUNEST_BASE}/lib/core/namespace.sh"
ID="id"
COMMON_BWRAP_OPTION="--bind "$NEST_HOME" / --bind "$HOME" "$HOME" --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try "/run/user/$($ID -u)" "/run/user/$($ID -u)" --unshare-user-try"
#######################################
# Run JuNest as fakeroot via bwrap
#
# Globals:
# NEST_HOME (RO) : The JuNest home directory.
# DEFAULT_SH (RO) : Contains the default command to run in Nest.
# BWRAP (RO): : The location of the bwrap binary.
# Arguments:
# backend_args ($1) : The arguments to pass to bwrap
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to Nest environment.
# cmd ($3-?) : The command to run inside Nest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_bwrap_fakeroot(){
local backend_command="${1:-$BWRAP}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
copy_common_files
fi
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Fix PATH to /usr/bin to make sudo working and avoid polluting with host related bin paths
PATH="/usr/bin" BWRAP="${backend_command}" bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}"
}
#######################################
# Run JuNest as normal user via bwrap.
#
# Globals:
# NEST_HOME (RO) : The JuNest home directory.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# BWRAP (RO): : The location of the bwrap binary.
# Arguments:
# backend_args ($1) : The arguments to pass to bwrap
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Output:
# - : The command output.
#######################################
function run_env_as_bwrap_user() {
local backend_command="${1:-$BWRAP}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup after the Nest directory creation
#copy_file /etc/localtime
copy_passwd_and_group
fi
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Resets PATH to avoid polluting with host related bin paths /
# In this script PATH=/usr/bin to avoid weird error messages
PATH='/usr/bin' BWRAP="${backend_command}" bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}"
}
### source "${JUNEST_BASE}/lib/core/proot.sh"
function _run_env_with_proot(){
local backend_command="${1:-$PROOT}"
local backend_args="$2"
shift 2
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Resets PATH to avoid polluting with host related bin paths
PATH='' PROOT="${backend_command}" proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}"
}
#######################################
# Run JuNest as fakeroot.
#
# Globals:
# NEST_HOME (RO) : The JuNest home directory.
# EUID (RO) : The user ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_proot_fakeroot(){
(( EUID == 0 )) && \
die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges."
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
copy_common_files
fi
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_with_proot "$backend_command" "-0 ${bindings} -r ${NEST_HOME} $backend_args" "$@"
}
#######################################
# Run Nest as normal user.
#
# Globals:
# NEST_HOME (RO) : The Nest home directory.
# EUID (RO) : The user ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_proot_user(){
(( EUID == 0 )) && \
die_on_status "$(gettext '$ROOT_ACCESS_ERROR" "You cannot access with root privileges.')"
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
# Files to bind are visible in `proot --help`.
# This function excludes /etc/mtab file so that
# it will not give conflicts with the related
# symlink in the Arch Linux image.
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup after the Nest directory creation
#copy_file /etc/localtime
copy_passwd_and_group
fi
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_with_proot "$backend_command" "${bindings} -r ${NEST_HOME} $backend_args" "$@"
}
### source "${JUNEST_BASE}/lib/core/wrappers.sh"
#######################################
# Create bin wrappers
#
# Globals:
# NEST_HOME (RO) : The JuNest home directory.
# Arguments:
# force ($1?) : Create bin wrappers even if the bin file exists.
# Defaults to false.
# Returns:
# None
# Output:
# None
#######################################
function create_wrappers() {
local force=${1:-false}
local bin_path=${2:-/usr/bin}
bin_path=${bin_path%/}
# Arguments inside a variable (i.e. `NEST_ARGS`) separated by quotes
# are not recognized normally unless using `eval`. More info here:
# https://github.com/fsquillace/junest/issues/262
# https://github.com/fsquillace/junest/pull/287
cat <<EOF > "${NEST_HOME}/usr/bin/nest_wrapper"
#!/usr/bin/env bash
eval "nest_args_array=(\${NEST_ARGS:-ns})"
nest "\${nest_args_array[@]}" -- \$(basename \${0}) "\$@"
EOF
chmod +x "${NEST_HOME}/usr/bin/nest_wrapper"
cd "${NEST_HOME}${bin_path}" || return 1
for file in *
do
[[ -d $file ]] && continue
# Symlinks outside junest appear as broken even though they are correct
# within a junest session. The following do not skip broken symlinks:
[[ -x $file || -L $file ]] || continue
if [[ -e ${NEST_HOME}${bin_path}_wrappers/$file ]] && ! $force
then
continue
fi
rm -f "${NEST_HOME}${bin_path}_wrappers/$file"
ln -s "${NEST_HOME}/usr/bin/nest_wrapper" "${NEST_HOME}${bin_path}_wrappers/$file"
done
# Remove wrappers no longer needed
cd "${NEST_HOME}${bin_path}_wrappers" || return 1
for file in *
do
[[ -e ${NEST_HOME}${bin_path}/$file || -L ${NEST_HOME}${bin_path}/$file ]] || rm -f "$file"
done
grep "$NEST_HOME${bin_path}_wrappers" /etc/profile.locale > /dev/null || echo "PATH=\$PATH:$NEST_HOME${bin_path}_wrappers" >> /etc/profile.locale
# The following loop allows applications that have an entry in the nested system menu to also have an entry in the host system menu
# if there is not already an entry for a such application in this menu. (Cancelled!)
# Problem: The categories in the menu differ between Devuan/Debian and EasyOS
#cd "${NEST_HOME}/usr/share/applications"
#for file in *
#do
#cp -n $file /usr/share/applications/
#done
}
###################################
### General functions ###
###################################
usage() {
echo -e "$NAME : $DESCRIPTION"
echo
echo -e "$(gettext 'Usage'): $CMD [action] [options] [--] [command]"
echo
echo -e "$(gettext 'General'):"
echo -e "-h, --help $(gettext 'Show this help message')"
echo
echo -e "$(gettext 'Actions and options'):"
echo -e " s[etup] $(eval_gettext 'Setup the Nest system in ${NEST_HOME}') (if not modified by the environment variable NEST_HOME or by the option -l)"
echo -e " -o, --option $(gettext 'Option for the debootstrap command. Each option must be prceded by a -o. See possible options on the man page https://linux.die.net/man/8/debootstrap'.) "
echo -e " -r, --release $(gettext 'Release code name. See /usr/share/debootstrap for the list of possible releases.The symbolic names (eg, unstable, testing,) corresponds to the distribution '.) "
echo -e " -m, --mirror $(gettext 'address of a repository that will provide the downloaded packages') "
echo -e " -l, --location <directory> $(gettext 'Setup the Nest system in the chosen directory'.) "
echo
echo -e " d[elete] $(eval_gettext 'Delete the Nest system from ${NEST_HOME} (or from the chosen directory if the option -l precedes)')"
echo -e " -l, --location <directory> $(gettext 'Delete the Nest system from the chosen directory'.) "
echo
echo -e " n[s] $(gettext 'Access via Linux Namespaces using BubbleWrap (Default action)')"
echo -e " -f, --fakeroot $(eval_gettext 'Run $NAME with fakeroot privileges')"
echo -e " --backend-command <cmd> $(gettext 'Bwrap command to use')"
echo -e " -b, --backend-args <args> $(gettext 'Arguments for bwrap backend program')"
echo -e " $(eval_gettext '($CMD ns -b \"--help\" to check out the bwrap options)')"
echo -e " -n, --no-copy-files $(eval_gettext '(Do not copy common etc files into $NAME environment')"
echo
echo -e " p[root] $(gettext 'Access via PRoot')"
echo -e " -f, --fakeroot $(eval_gettext 'Run $NAME with fakeroot privileges')"
echo -e " --backend-command <cmd> $(gettext 'PRoot command to use')"
echo -e " -b, --backend-args <args> $(gettext 'Arguments for PRoot backend program')"
echo -e " $(eval_gettext '($CMD proot -b \"--help\" to check out the PRoot options)')"
echo -e " -n, --no-copy-files $(eval_gettext '(Do not copy common etc files into $NAME environment')"
echo
echo -e " r[oot] $(gettext 'Access with root privileges via classic chroot')"
echo -e " --backend-command <cmd> $(gettext 'Chroot command to use')"
echo -e " -b, --backend-args <args> $(gettext 'Arguments for chroot backend program')"
echo -e " $(eval_gettext '($CMD root -b \"--help\" to check out the chroot options)')"
echo -e " -n, --no-copy-files $(eval_gettext 'Do not copy common etc files into $NAME environment')"
echo
echo -e " create-bin-wrappers $(gettext 'Create a bin wrappers directory according to --bin-path option')"
echo -e " $(eval_gettext 'Default path is $NEST_HOME/usr/bin_wrappers')"
echo -e " -f, --force $(gettext 'Create the wrapper files even if they already exist')"
echo -e " -p, --bin-path $(gettext 'The source directory where executable are located in Nest')"
echo -e " $(gettext 'Default value is: /usr/bin')"
echo
}
function parse_arguments(){
# Actions
ACT_SETUP=false
ACT_DELETE=false
ACT_CREATE_WRAPPERS=false
ACT_NAMESPACE=false
ACT_PROOT=false
ACT_ROOT=false
ACT_HELP=false
case "$1" in
s|setup) ACT_SETUP=true ; shift ;;
d|delete) ACT_DELETE=true ; shift ;;
create-bin-wrappers) ACT_CREATE_WRAPPERS=true ; shift ;;
n|ns) ACT_NAMESPACE=true ; shift ;;
p|proot) ACT_PROOT=true ; shift ;;
r|root) ACT_ROOT=true ; shift ;;
-h|--help) ACT_HELP=true ; shift ;;
*) ACT_NAMESPACE=true ;;
esac
if $ACT_SETUP
then
_parse_setup_opts "$@"
elif $ACT_CREATE_WRAPPERS
then
_parse_create_wrappers_opts "$@"
elif $ACT_NAMESPACE
then
_parse_ns_opts "$@"
elif $ACT_PROOT
then
_parse_proot_opts "$@"
elif $ACT_ROOT
then
_parse_root_opts "$@"
fi
}
function _parse_root_opts() {
# Options:
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
while [[ -n "$1" ]]
do
case "$1" in
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
}
function _parse_ns_opts() {
# Options:
OPT_FAKEROOT=false
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
}
function _parse_proot_opts() {
# Options:
OPT_FAKEROOT=false
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
ARGS=("$@")
}
function _parse_create_wrappers_opts() {
OPT_FORCE=false
OPT_BIN_PATH=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--force) OPT_FORCE=true ; shift ;;
-p|--bin-path) shift ; OPT_BIN_PATH="$1" ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function _parse_setup_opts() {
while [[ -n "$1" ]]
do
case "$1" in
-r|--release) shift ; RELEASE=$1 ; NEST_HOME=/$1 ; shift ;;
-o|option) shift ; OPTIONS="$OPTIONS $1" ; shift ;;
-m|--mirror) shift ; MIRROR=$1 ; shift ;;
-l|--location) shift ; NEST_HOME=$1 ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function _parse_delete_opts() {
while [[ -n "$1" ]]
do
case "$1" in
-l|--location) shift ; NEST_HOME=$1 ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function execute_operation() {
$ACT_HELP && usage && return
if $ACT_SETUP; then
#if is_env_installed
#then
#die "Error: The image cannot be installed since $NEST_HOME is not empty."
#fi
setup_env
create_wrappers
return
fi
if $ACT_DELETE; then
delete_env
return
fi
if ! is_env_installed
then
die "Error: The image is still not installed in $NEST_HOME. Run this first: $CMD setup"
fi
if $ACT_CREATE_WRAPPERS; then
create_wrappers $OPT_FORCE "$OPT_BIN_PATH"
exit
fi
local run_env
if $ACT_NAMESPACE; then
if $OPT_FAKEROOT; then
run_env=run_env_as_bwrap_fakeroot
else
run_env=run_env_as_bwrap_user
fi
elif $ACT_PROOT; then
if $OPT_FAKEROOT; then
run_env=run_env_as_proot_fakeroot
else
run_env=run_env_as_proot_user
fi
elif $ACT_ROOT; then
run_env=run_env_as_chroot
fi
# Call create_wrappers in case new bin files have been created
if [ "${USER}" == "root" ]
then
trap "PATH=$PATH create_wrappers" EXIT QUIT TERM
fi
$run_env "$BACKEND_COMMAND" "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}"
}
function main() {
parse_arguments "$@"
execute_operation
}
main "$@"
.
It is possible to choose another release (of Devuan/Debian/Ubuntu) and another install directory by modifying the script (It's at the beginning of the script) or by using environment variable (Eg the install directory is NEST_HOME)
The script creates the file profile.locale) (if it doesn't exist) in /etc. It writes a line to it that adds the directory $NEST_HOME/usr/bin_wrappers to the PATH, so it will be possible to launch an executable in the nested Devuan as a command in EasyOS.
As the categories in the desktop menu differs betwwen EasyOs and Devuan, it is not possible to integrate automatically the (graphical) applications of Devuan in the menu of EasyOS.
Unfortunately this minimal system is big (more than 1500 MB)
There are other options for debootstrap and even variants of debootstrap : cdebootsrap, mmdebootstrap grml-debootstrap with other options
But for now I will not pursue my tests in this direction further.
Edit : fix in the script : There was missing dots line 456
Edit2: the line 786 was lightly modified
EDIT3: In EasyOS the file /root/.bash_profile is not used. It was replaced by /etc/profile.local in the script