#!/bin/sh
# A simple systemd-nspawn wrapper for chroot
# Copyright (C) 2017 Márcio Silva <coadde@hyperbola.info>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Set default variables
_LAST_OPTION=command
_SET_VERSION=0.2.4

#
_error_doesnt_allow_an_argument() {
	case $_OPTION			in
		--a*=*)			local _ERROR=--as-pid2		;;
		--bo*=*)		local _ERROR=--boot		;;
		--e*=*)			local _ERROR=--ephemeral	;;
		--h*=*)			local _ERROR=--help		;;
		--ke*=*)		local _ERROR=--keep-unit	;;
		--network-veth=*)	local _ERROR=--network-veth	;;
		--private-n*=*)		local _ERROR=--private-network	;;
		--q*=*)			local _ERROR=--quiet		;;
		--rea*=*)		local _ERROR=--read-only	;;
		--sete*=*)		local _ERROR=--setenv		;;
		--sh*=*)		local _ERROR=--share-system	;;
	esac
	echo ${0#./}: option \'$_ERROR\' doesn\'t allow an argument
	exit 1
}

#
_error_invalid_option() {
	local _ERROR=${_OPTION:(($_NC-1)):1}
	echo ${0#./}: invalid option -- \'$_ERROR\'
	exit 1
}

#
_error_option_is_ambiguous() {
	local _PBOOT="'--boot'"
	local _PBIRW="'--bind'"
	local _PBIRO="'--bind-ro'"
	local _PCAPA="'--capability'"
	local _PCHDI="'--chdir'"
	local _PDIRE="'--directory'"
	local _PDROP="'--drop-capability'"
	local _PKEEP="'--keep-unit'"
	local _PKILL="'--kill-signal'"
	local _PNEBR="'--network-bridge'"
	local _PNEIN="'--network-interface'"
	local _PNEIP="'--network-ipvlan'"
	local _PNEMA="'--network-macvlan'"
	local _PNEV_="'--network-veth'"
	local _PNEVE="'--network-veth-extra'"
	local _PNEZO="'--network-zone'"
	local _PNOTI="'--notify-ready'"
	local _POVRW="'--overlay'"
	local _POVRO="'--overlay-ro'"
	local _PPERS="'--personality'"
	local _PPORT="'--port'"
	local _PPRNE="'--private-network'"
	local _PPRU_="'--private-users'"
	local _PPRUC="'--private-users-chown'"
	local _PPROP="'--property'"
	local _PREAD="'--read-only'"
	local _PREGI="'--register'"
	local _PSE_C="'--selinux-context'"
	local _PSEAC="'--selinux-apifs-context'"
	local _PSETE="'--setenv'"
	local _PSETT="'--settings'"
	local _PSHAR="'--share-system'"
	local _PSLIC="'--slice'"
	local _PTEMP="'--template'"
	local _PTMPF="'--tmpfs'"
	local _PUSER="'--user'"
	local _PUUID="'--uuid'"
	local _PVERS="'--version'"
	local _PVOLA="'--volatile'"
	case $_OPTION		in
		--b)		local _ERROR=$_PBOOT" "$_PBIRO" "$_PBIRW							;;
		--bi*)		local _ERROR=$_PBIRO" "$_PBIRW									;;
		--c*)		local _ERROR=$_PCAPA" "$_PCHDI									;;
		--d*)		local _ERROR=$_PDIRE" "$_PDROP									;;
		--k*)		local _ERROR=$_PKEEP" "$_PKILL									;;
		--n)		local _ERROR=$_PNEIN" "$_PNOTI" "$_PNEZO" "$_PNEBR" "$_PNEVE" "$_PNEV_" "$_PNEIP" "$_PNEMA	;;
		--ne|--net)													;&
		--netw|--netwo)													;&
		--networ)													;&
		--network)													;&
		--network-)	local _ERROR=$_PNEIN" "$_PNEZO" "$_PNEBR" "$_PNEVE" "$_PNEV_" "$_PNEIP" "$_PNEMA		;;
		--network-i*)	local _ERROR=$_PNEIN" "$_PNEIP									;;
		--network-v*)	local _ERROR=$_PNEV_" "$_PNEVE									;;
		--o*)		local _ERROR=$_POVRW" "$_POVRO									;;
		--p)		local _ERROR=$_PPRNE" "$_PPRUC" "$_PPRU_" "$_PPROP" "$_PPORT" "$_PPERS				;;
		--pr)		local _ERROR=$_PPRNE" "$_PPRUC" "$_PPRU_" "$_PPROP						;;
		--pri|--priv)													;&
		--priva)													;&
		--privat)													;&
		--private)													;&
		--private-)	local _ERROR=$_PPRNE" "$_PPRUC" "$_PPRU_							;;
		--private-u*)	local _ERROR=$_PPRU_" "$_PPRUC									;;
		--r*)		local _ERROR=$_PREAD" "$_PREGI									;;
		--s)		local _ERROR=$_PSLIC" "$_PSETT" "$_PSHAR" "$_PSEAC" "$_PSE_C" "$_PSETE				;;
		--se)		local _ERROR=$_PSETE" "$_PSETT" "$_PSEAC" "$_PSE_C						;;
		--sel*)		local _ERROR=$_PSE_C" "$_PSEAC									;;
		--set*)		local _ERROR=$_PSETE" "$_PSETT									;;
		--t*)		local _ERROR=$_PTEMP" "$_PTMPF									;;
		--u*)		local _ERROR=$_PUSER" "$_PUUID									;;
		--v*)		local _ERROR=$_PVERS" "$_PVOLA									;;
	esac
	echo ${0#./}: option \'$_OPTION\' is ambiguous\; possibilities: $_ERROR
	exit 1
}

#
_error_requires_an_argument() {
	case $_OPTION			in
		--bind)			local _ERROR=--bind			;;
		--bind-*)		local _ERROR=--bind-ro			;;
		--ca*)			local _ERROR=--capability		;;
		--ch*)			local _ERROR=--chdir			;;
		--di*)			local _ERROR=--directory		;;
		--dr*)			local _ERROR=--drop-capability		;;
		--i*)			local _ERROR=--image			;;
		--ki*)			local _ERROR=--kill-signal		;;
		--l*)			local _ERROR=--link-journal		;;
		--m*)			local _ERROR=--machine			;;
		--network-b*)		local _ERROR=--network-bridge		;;
		--network-in*)		local _ERROR=--network-interface	;;
		--network-ip*)		local _ERROR=--network-ipvlan		;;
		--network-m*)		local _ERROR=--network-macvlan		;;
		--network-z*)		local _ERROR=--network-zone		;;
		--network-veth-*)	local _ERROR=--network-veth-extra	;;
		--no*)			local _ERROR=--notify-ready		;;
		--overlay)		local _ERROR=--overlay			;;
		--overlay-*)		local _ERROR=--overlay-ro		;;
		--pe*)			local _ERROR=--personality		;;
		--po*)			local _ERROR=--port			;;
		--pro*)			local _ERROR=--property			;;
		--reg*)			local _ERROR=--register			;;
		--selinux-a*)		local _ERROR=--selinux-apifs-context	;;
		--selinux-c*)		local _ERROR=--selinux-context		;;
		--sett*)		local _ERROR=--settings			;;
		--sl*)			local _ERROR=--slice			;;
		--te*)			local _ERROR=--template			;;
		--tm*)			local _ERROR=--tmpfs			;;
		--us*)			local _ERROR=--user			;;
		--uu*)			local _ERROR=--uuid			;;
		--ve*)			local _ERROR=--version			;;
		--vo*)			local _ERROR=--volatile			;;
	esac
	echo ${0#./}: option \'$_ERROR\' requires an argument
	exit 1
}

# Function to print error "unrecognized option" and exit with error code
_error_unrecognized_option() {
	echo ${0#./}: unrecognized option \'$_OPTION\'
	exit 1
}

#
_option_as_pid2() {
	_USE_AS_PID2=true
}

#
_option_bind() {
	local _SET_OPTION_ALL=${_OPTION#--bind*=}
	local _SET_OPTION_FST=${_SET_OPTION%%:*}
	case $_OPTION		in
		--bind*=/*)	if   [ ! -d $_SET_OPTION_FST ]; then
					echo Failed to stat $_SET_OPTION_FST: No such file or directory
					exit 1
				fi
				;;&
		--bind*=[!/]*)	echo Failed to parse --bind\(-ro\)= argument $_SET_OPTION_FST: Invalid argument
				exit 1
				;;
		--bind=*)	_SET_BIND_RW+=($_SET_OPTION_ALL)	;;
		--bind-*=*)	_SET_BIND_RO+=($_SET_OPTION_ALL)	;;
	esac
}

#
_option_boot() {
	_USE_BOOT=true
}

#
_option_capability() {
	_SET_CAPABILITY=${_OPTION#--ca*=}
}

#
_option_chdir() {
	_SET_CHDIR=${_OPTION#--ch*=}
}

#
_option_directory() {
	case $_OPTION	in
		--di*)
			_USE_DIRECTORY=true
			_SET_DIRECTORY=${_OPTION#--di*=}
			;;
		*)
			case $_LAST_OPTION	in
				directory)
						_USE_DIRECTORY=true
						_SET_DIRECTORY=${_OPTION}
						;;
				*)		_SET_DIRECTORY=$PWD		;;
			esac
			;;
	esac
	_LAST_OPTION=command
}

#
_option_drop_capability() {
	_SET_DROP_CAPABILITY=${_OPTION#--dr*=}
}

#
_option_ephemeral() {
	_USE_EPHEMERAL=true
}

# Function to print the help script and exit successfully
_option_help() {
	echo -e \
${0#./}' [OPTIONS...] [PATH] [ARGUMENTS...]''\n\n'\
'Spawn a minimal namespace container for debugging, testing and building.''\n\n'\
'  -h --help			Show this help''\n'\
'     --version			Print version string''\n'\
'  -q --quiet			Do not show status information''\n'\
'  -D --directory=PATH		Root directory for the container''\n'\
'     --private-network         Disable network in container''\n'\
'     --bind=PATH[:PATH[:OPTIONS]]''\n'\
'                               Bind mount a file or directory from the host into''\n'\
'                               the container''\n'\
'     --bind-ro=PATH[:PATH[:OPTIONS]]''\n'\
'                               Similar, but creates a read-only bind mount''\n'\
'     --template=PATH''\n'\
'  -x --ephemeral''\n'\
'  -i --image=PATH''\n'\
'  -a --as-pid2''\n'\
'  -b --boot''\n'\
'     --chdir=PATH''\n'\
'  -u --user=USER''\n'\
'  -M --machine=NAME''\n'\
'     --uuid=UUID''\n'\
'  -S --slice=SLICE''\n'\
'     --property=NAME=VALUE''\n'\
'  -U --private-users=pick''\n'\
'     --private-users[=UIDBASE[:NUIDS]]''\n'\
'     --private-users-chown''\n'\
'     --network-interface=INTERFACE''\n'\
'     --network-macvlan=INTERFACE''\n'\
'     --network-ipvlan=INTERFACE''\n'\
'  -n --network-veth''\n'\
'     --network-veth-extra=HOSTIF[:CONTAINERIF]''\n'\
'     --network-bridge=INTERFACE''\n'\
'     --network-zone=NAME''\n'\
'  -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]''\n'\
'  -Z --selinux-context=SECLABEL''\n'\
'  -L --selinux-apifs-context=SECLABEL''\n'\
'     --capability=CAP''\n'\
'     --drop-capability=CAP''\n'\
'     --kill-signal=SIGNAL''\n'\
'     --link-journal=MODE''\n'\
'  -j''\n'\
'     --read-only''\n'\
'     --tmpfs=PATH:[OPTIONS]''\n'\
'     --overlay=PATH[:PATH...]:PATH''\n'\
'     --overlay-ro=PATH[:PATH...]:PATH''\n'\
'  -E --setenv=NAME=VALUE''\n'\
'     --register=BOOLEAN''\n'\
'     --keep-unit''\n'\
'     --volatile[=MODE]''\n'\
'     --settings=BOOLEAN''\n'\
'     --notify-ready=BOOLEAN''\n'\
'     --personality''\n'\
'     --share-system''\n'\
'				Does nothing'

#'#    --template=PATH		Initialize root directory from template directory,''\n'\
#'#				if missing''\n'\
#'# -x --ephemeral		Run container with snapshot of root directory, and''\n'\
#'#				remove it after exit''\n'\
#'# -i --image=PATH		File system device or disk image for the container''\n'\
#'# -a --as-pid2			Maintain a stub init as PID1, invoke binary as PID2''\n'\
#'# -b --boot			Boot up full system (i.e.invoke init)''\n'\
#'#    --chdir=PATH		Set working directory in the container''\n'\
#'# -u --user=USER		Run the command under specified user or uid''\n'\
#'# -M --machine=NAME		Set the machine name for the container''\n'\
#'#    --uuid=UUID		Set a specific machine UUID for the container''\n'\
#'# -S --slice=SLICE		Place the container in the specified slice''\n'\
#'#    --property=NAME=VALUE	Set scope unit property''\n'\
#'# -U --private-users=pick	Run within user namespace, autoselect UID/GID range''\n'\
#'#    --private-users[=UIDBASE[:NUIDS]]''\n'\
#'#				Similar, but with user configured UID/GID range''\n'\
#'#    --private-users-chown	Adjust OS tree ownership to private UID/GID range''\n'\
#'     --private-network		Disable network in container''\n'\
#'#    --network-interface=INTERFACE''\n'\
#'#				Assign an existing network interface to the''\n'\
#'#				container''\n'\
#'#    --network-macvlan=INTERFACE''\n'\
#'#				Create a macvlan network interface based on an''\n'\
#'#				existing network interface to the container''\n'\
#'#    --network-ipvlan=INTERFACE''\n'\
#'#				Create a ipvlan network interface based on an''\n'\
#'#				existing network interface to the container''\n'\
#'# -n --network-veth		Add a virtual Ethernet connection between host''\n'\
#'#				and container''\n'\
#'#    --network-veth-extra=HOSTIF[:CONTAINERIF]''\n'\
#'#				Add an additional virtual Ethernet link between''\n'\
#'#				host and container''\n'\
#'#    --network-bridge=INTERFACE''\n'\
#'#				Add a virtual Ethernet connection to the container''\n'\
#'#				and attach it to an existing bridge on the host''\n'\
#'#    --network-zone=NAME	Similar, but attach the new interface to an''\n'\
#'#				automatically managed bridge interface''\n'\
#'# -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]''\n'\
#'#				Expose a container IP port on the host''\n'\
#'# -Z --selinux-context=SECLABEL''\n'\
#'#				Set the SELinux security context to be used by''\n'\
#'#				processes in the container''\n'\
#'# -L --selinux-apifs-context=SECLABEL''\n'\
#'#				Set the SELinux security context to be used by''\n'\
#'#				API/tmpfs file systems in the container''\n'\
#'#    --capability=CAP		In addition to the default, retain specified''\n'\
#'#				capability''\n'\
#'#    --drop-capability=CAP	Drop the specified capability from the default set''\n'\
#'#    --kill-signal=SIGNAL	Select signal to use for shutting down PID 1''\n'\
#'#    --link-journal=MODE	Link up guest journal, one of no, auto, guest,''\n'\
#'#				host, try-guest, try-host''\n'\
#'# -j				Equivalent to --link-journal=try-guest''\n'\
#'#    --read-only		Mount the root directory read-only''\n'\
#'     --bind=PATH[:PATH[:OPTIONS]]''\n'\
#'				Bind mount a file or directory from the host into''\n'\
#'				the container''\n'\
#'     --bind-ro=PATH[:PATH[:OPTIONS]]''\n'\
#'				Similar, but creates a read-only bind mount''\n'\
#'#    --tmpfs=PATH:[OPTIONS]	Mount an empty tmpfs to the specified directory''\n'\
#'#    --overlay=PATH[:PATH...]:PATH''\n'\
#'#				Create an overlay mount from the host to''\n'\
#'#				the container''\n'\
#'#    --overlay-ro=PATH[:PATH...]:PATH''\n'\
#'#				Similar, but creates a read-only overlay mount''\n'\
#'# -E --setenv=NAME=VALUE	Pass an enviroment variable to PID 1''\n'\
#'#    --register=BOOLEAN		Register container as machine''\n'\
#'#    --keep-unit		Do not register a scope for the machine, reuse''\n'\
#'#				the service unit nspawn is running in''\n'\
#'#    --volatile[=MODE]		Run the system in volatile mode''\n'\
#'#    --settings=BOOLEAN		Load additional settings from .nspawn file''\n'\
#'#    --notify-ready=BOOLEAN	Receive notifications from the child init process'
	exit 0
}

#
_option_image() {
	_USE_IMAGE=true
	case $_OPTION	in
		--i*)	local _SET_IMAGE=${_OPTION#--i*=}			;;
		*)
			case $_LAST_OPTION	in
				image)		local _SET_IMAGE=${_OPTION}	;;
				*)						;;
			esac
			;;
	esac
	_LAST_OPTION=command
}

#
_option_keep_unit() {
	_USE_KEEP_UNIT=true
}

#
_option_kill_signal() {
	_SET_KILL_SIGNAL=${_OPTION#--ki*=}
}

#
_option_link_journal() {
	case $_OPTION		in
		--l*)		_SET_LINK_JOURNAL=${_OPTION#--l*=}	;;
		*)		_SET_LINK_JOURNAL=try-guest		;;
	esac
	case $_SET_LINK_JOURNAL	in
		auto)							;;
		guest)							;;
		host)							;;
		no)							;;
		try-guest)						;;
		try-host)						;;
		*)							;;
	esac
}

#
_option_machine() {
	case $_OPTION	in
		--m*)	_SET_MACHINE=${_OPTION#--m*=}			;;
		*)
			case $_LAST_OPTION	in
				machine)	_SET_MACHINE=${_OPTION}	;;
				*)					;;
			esac
			;;
	esac
	_LAST_OPTION=command
}

#
_option_network_bridge() {
	_SET_NETWORK_BRIDGE=${_OPTION#--network-b*=}
}

#
_option_network_interface() {
	_SET_NETWORK_INTERFACE=${_OPTION#--network-in*=}
}

#
_option_network_ipvlan() {
	_SET_NETWORK_IPVLAN=${_OPTION#--network-ip*=}
}

#
_option_network_macvlan() {
	_SET_NETWORK_MACVLAN=${_OPTION#--network-m*=}
}

#
_option_network_veth() {
	local _NEEDS_CHECK=
}

#
_option_network_veth_extra() {
	local _NEEDS_CHECK=
	_SET_NETWORK_VETH_EXTRA=${_OPTION#--network-veth-*=}
}

#
_option_network_zone() {
	_SET_NETWORK_ZONE=${_OPTION#--network-z*=}
}

#
_option_notify_ready() {
	_SET_NOTIFY_READY=${_OPTION#--no*=}
}

#
_option_overlay() {
	case $_OPTION		in
		--overlay)	_SET_OVERLAY_RW=(${_OPTION#--overlay*=})	;;
		--overlay-*)	_SET_OVERLAY_RO=(${_OPTION#--overlay*=})	;;
	esac
}

#
_option_personality() {
	local _SET_PETSONALITY=${_OPTION#--pe*=}
	case $_SET_PETSONALITY	in
		x86)		;;
		x86-64)		;;
		*)
				echo Unknown or unsupported personality \'$_SET_PETSONALITY\'
				exit 1
				;;
	esac
}

#
_option_port() {
	case $_OPTION	in
		--po*)	_SET_PORT=${_OPTION#--po*=}	;;
		*)	_SET_PORT=${_OPTION}		;;
	esac
	_LAST_OPTION=command
}

#
_option_private_network() {
	_SET_PRIVATE_NETWORK=true
}

#
_option_private_users() {
	case $_OPTION			in
		--private-users)	_SET_PRIVATE_USERS=${_OPTION#--private-users*=}	;;
		*)			_SET_PRIVATE_USERS=pick				;;
	esac
	case $_SET_PRIVATE_USERS	in
		no)									;;
		pick)									;;
		*)									;;
	esac
}

#
_option_property() {
	_SET_PROPERTY=${_OPTION#--pro*=}
}

#
_option_quiet() {
	_USE_QUIET=true
}

#
_option_read_only() {
	_USE_READ_ONLY=true
}

#
_option_register() {
	case $_OPTION		in
		--reg*)		_SET_REGISTER=${_OPTION#--reg*=}	;;
		*)		_SET_REGISTER=yes			;;
	esac
	case $_SET_REGISTER	in
		yes)							;;
		no)							;;
		*)							;;
	esac
}

#
_option_selinux_context() {
	local _NEEDS_CHECK=true
	_LAST_OPTION=command
}

#
_option_setenv() {
	_SET_SETENV=${_OPTION#--sete*=}
	_LAST_OPTION=command
}

#
_option_settings() {
	_SET_SETTINGS=${_OPTION#--sett*=}
	case $_SET_SETTINGS						in
		0|[Nn]|[Nn][Oo]|[Oo][Ff][Ff]|[Ff]|[Ff][Aa][Ll][Ss][Ee])	;;
		1|[Yy]|[Yy][Ee][Ss]|[Oo][Nn]|[Tt]|[Tt][Rr][Uu][Ee])	;;
		override)						;;
		trusted)						;;
		*)							;;
	esac
}

#
_option_share_system() {
	_USE_SHARE_SYSTEM=true
}

#
_option_slice() {
	_SET_SLICE=${_OPTION#--sl*=}
	_LAST_OPTION=command
}

#
_option_template() {
	_USE_TEMPLATE=true
	_SET_TEMPLATE=${_OPTION#--te*=}
}

#
_option_tmpfs() {
	_SET_TMPFS=${_OPTION#--tm*=}
}

#
_option_user() {
	_SET_USER=${_OPTION#--us*=}
	_LAST_OPTION=command
}

#
_option_uuid() {
	_SET_UUID=${_OPTION#--uu*=}
}

# Function to print the version script and exit successfully
_option_version() {
	echo ${0#./} $_SET_VERSION
	exit 0
}

#
_option_volatile() {
	_SET_VOLATILE=${_OPTION#--vo*=}
}

#
_run_bind() {
	local _MOUNT=
	for _MOUNT in ${_SET_BIND_RO[@]}; do
		local _MOUNT_ST1=${_MOUNT#*:}
		local _MOUNT_ST2=${_MOUNT_ST1#*:}
		local _MOUNT_SRC=${_MOUNT%%:*}
		local _MOUNT_DST=${_MOUNT_ST1%%:*}
		local _MOUNT_OPT=${_MOUNT_ST2%%:*}
		if   [ -d $_SET_DIRECTORY/$_MOUNT_DST ]; then
			mount -B		$_MOUNT_SRC	$_SET_DIRECTORY/$_MOUNT_DST
			if   [ $_MOUNT_OPT = $_MOUNT_DST ]; then
				mount -o remount,ro		$_SET_DIRECTORY/$_MOUNT_DST
			else
				mount -o remount,$_MOUNT_OPT,ro	$_SET_DIRECTORY/$_MOUNT_DST
			fi
			mount --make-slave			$_SET_DIRECTORY/$_MOUNT_DST
		fi
	done
	for _MOUNT in ${_SET_BIND_RW[@]}; do
		local _MOUNT_ST1=${_MOUNT#*:}
		local _MOUNT_ST2=${_MOUNT_ST1#*:}
		local _MOUNT_SRC=${_MOUNT%%:*}
		local _MOUNT_DST=${_MOUNT_ST1%%:*}
		local _MOUNT_OPT=${_MOUNT_ST2%%:*}
		if   [ -d $_SET_DIRECTORY/$_MOUNT_DST ]; then
			mount -B		$_MOUNT_SRC	$_SET_DIRECTORY/$_MOUNT_DST
			if   [ $_MOUNT_OPT = $_MOUNT_DST ]; then
				mount -o remount,rw		$_SET_DIRECTORY/$_MOUNT_DST
			else
				mount -o remount,$_MOUNT_OPT,rw	$_SET_DIRECTORY/$_MOUNT_DST
			fi
			mount --make-slave			$_SET_DIRECTORY/$_MOUNT_DST
		fi
	done
	# source:destination:options
	# error no destination
	# execv() failed: No such file or directory
}

# Function to umount directories and exit the script with error code
_trap_error() {
	local _LOCAL_ERROR_CODE=$_ERROR_CODE
	umount	-R $_SET_DIRECTORY
	case $_USE_QUIET	in
		true)		;;
		false)		echo Container ${_SET_DIRECTORY##*/} exited with error code $_LOCAL_ERROR_CODE.
				;;
	esac
	unset _ERROR_CODE _SET_DIRECTORY _USE_QUIET
	exit $_LOCAL_ERROR_CODE
}

# Function to umount directories and exit the script successfully
_trap_exit() {
	umount	-R $_SET_DIRECTORY
	case $_USE_QUIET	in
		true)		;;
		false)		echo Container ${_SET_DIRECTORY##*/} exited successfully.
				;;
	esac
	unset _ERROR_CODE _SET_DIRECTORY _USE_QUIET
	exit 0
}

#
_option_directory

#
for _OPTION in $@; do
	_ALL_OPTIONS+=' '$_OPTION
done
unset _OPTION

# Assign a variable from current arguments
for _OPTION in $@; do
	_REMOVE_OPTIONS+=' '$_OPTION
	case $_OPTION				in
		--a|--as|--as-|--as-p|--as-pi)					;&
		--as-pid|--as-pid2)		_option_as_pid2			;;
		--a=*|--as=*|--as-=*)						;&
		--as-p=*|--as-pi=*)						;&
		--as-pid=*|--as-pid2=*)		_error_doesnt_allow_an_argument	;;
		--b|--b=*|--bi|--bi=*)						;&
		--bin|--bin=*)			_error_option_is_ambiguous	;;
		--bind)								;&
		--bind-|--bind-r|--bind-ro)	_error_requires_an_argument	;;
		--bind=*)							;&
		--bind-=*|--bind-r=*)						;&
		--bind-ro=*)			_option_bind			;;
		--bo|--boo|--boot)		_option_boot			;;
		--bo=*|--boo=*|--boot=*)	_error_doesnt_allow_an_argument	;;
		--c|--c=*)			_error_option_is_ambiguous	;;
		--ca|--cap|--capa|--capab)					;&
		--capabi|--capabil|--capabili)					;&
		--capabilit|--capability)	_error_requires_an_argument	;;
		--ca=*|--cap=*|--capa=*)					;&
		--capab=*|--capabi=*)						;&
		--capabil=*|--capabili=*)					;&
		--capabilit=*|--capability=*)	_option_capability		;;
		--ch|--chd|--chdi|--chdir)	_error_requires_an_argument	;;
		--ch=*|--chd=*|--chdi=*)					;&
		--chdir=*)			_option_chdir			;;
		--d|--d=*)			_error_option_is_ambiguous	;;
		--di|--dir|--dire)						;&
		--direc|--direct|--directo)					;&
		--director|--directory)		_error_requires_an_argument	;;
		--di=*|--dir=*)							;&
		--dire=*|--direc=*)						;&
		--direct=*|--directo=*)						;&
		--director=*|--directory=*)	_option_directory		;;
		--dr|--dro|--drop|--drop-)					;&
		--drop-c|--drop-ca|--drop-cap)					;&
		--drop-capa|--drop-capab)					;&
		--drop-capabi|--drop-capabil)					;&
		--drop-capabili)						;&
		--drop-capabilit)						;&
		--drop-capability)		_error_requires_an_argument	;;
		--dr=*|--dro=*|--drop=*)					;&
		--drop-=*|--drop-c=*)						;&
		--drop-ca=*|--drop-cap=*)					;&
		--drop-capa=*|--drop-capab=*)					;&
		--drop-capabi=*)						;&
		--drop-capabil=*)						;&
		--drop-capabili=*)						;&
		--drop-capabilit=*)						;&
		--drop-capability=*)		_option_drop_capability		;;
		--e|--ep|--eph|--ephe|--ephem)					;&
		--epheme|--ephemer|--ephemera)					;&
		--ephemeral)			_option_ephemeral		;;
		--e=*|--ep=*|--eph=*|--ephe=*)					;&
		--ephem=*|--epheme=*)						;&
		--ephemer=*|--ephemera=*)					;&
		--ephemeral=*)			_error_doesnt_allow_an_argument	;;
		--h|--he|--hel|--help)		_option_help			;;
		--h=*|--he=*|--hel=*|--help=*)	_error_doesnt_allow_an_argument	;;
		--i|--im|--ima|--imag|--image)	_error_requires_an_argument	;;
		--i=*|--im=*|--ima=*|--imag=*)					;&
		--image=*)			_option_image			;;
		--k|--k=*)			_error_option_is_ambiguous	;;
		--ke|--kee|--keep|--keep-)					;&
		--keep-u|--keep-un|--keep-uni)					;&
		--keep-unit)			_option_keep_unit		;;
		--ke=*|--kee=*|--keep=*)					;&
		--keep-=*|--keep-u=*)						;&
		--keep-un=*|--keep-uni=*)					;&
		--keep-unit=*)			_error_doesnt_allow_an_argument	;;
		--ki|--kil|--kill|--kill-)					;&
		--kill-s|--kill-si|--kill-sig)					;&
		--kill-sign|--kill-signa)					;&
		--kill-signal)			_error_requires_an_argument	;;
		--ki=*|--kil=*|--kill=*)					;&
		--kill-=*|--kill-s=*)						;&
		--kill-si=*|--kill-sig=*)					;&
		--kill-sign=*|--kill-signa=*)					;&
		--kill-signal=*)		_option_kill_signal		;;
		--l|--li|--lin|--link|--link-)					;&
		--link-j|--link-jo|--link-jou)					;&
		--link-jour|--link-journ)					;&
		--link-journa|--link-journal)	_error_requires_an_argument	;;
		--l=*|--li=*|--lin=*)						;&
		--link=*|--link-=*)						;&
		--link-j=*|--link-jo=*)						;&
		--link-jou=*|--link-jour=*)					;&
		--link-journ=*|--link-journa=*)					;&
		--link-journal=*)		_option_link_journal		;;
		--m|--ma|--mac|--mach|--machi)					;&
		--machin|--machine)		_error_requires_an_argument	;;
		--m=*|--ma=*|--mac=*)						;&
		--mach=*|--machi=*)						;&
		--machin=*|--machine=*)		_option_machine			;;
		--n|--n=*)							;&
		--ne|--ne=*|--net|--net=*)					;&
		--netw|--netw=*|--netwo)					;&
		--netwo=*|--networ|--networ=*)					;&
		--network|--network=*)						;&
		--network-|--network-=*)	_error_option_is_ambiguous	;;
		--network-b|--network-br)					;&
		--network-bri|--network-brid)					;&
		--network-bridg)						;&
		--network-bridge)		_error_requires_an_argument	;;
		--network-b=*|--network-br=*)					;&
		--network-bri=*)						;&
		--network-brid=*)						;&
		--network-bridg=*)						;&
		--network-bridge=*)		_option_network_bridge		;;
		--network-i|--network-i=*)	_error_option_is_ambiguous	;;
		--network-in|--network-int)					;&
		--network-inte|--network-inter)					;&
		--network-interf)						;&
		--network-interfa)						;&
		--network-interfac)						;&
		--network-interface)		_error_requires_an_argument	;;
		--network-in=*|--network-int=*)					;&
		--network-inte=*)						;&
		--network-inter=*)						;&
		--network-interf=*)						;&
		--network-interfa=*)						;&
		--network-interfac=*)						;&
		--network-interface=*)		_option_network_interface	;;
		--network-ip|--network-ipv)					;&
		--network-ipvl|--network-ipvla)					;&
		--network-ipvlan)		_error_requires_an_argument	;;
		--network-ip=*|--network-ipv=*)					;&
		--network-ipvl=*)						;&
		--network-ipvla=*)						;&
		--network-ipvlan=*)		_option_network_ipvlan		;;
		--network-m|--network-ma)					;&
		--network-mac|--network-macv)					;&
		--network-macvl)						;&
		--network-macvla)						;&
		--network-macvlan)		_error_requires_an_argument	;;
		--network-m=*|--network-ma=*)					;&
		--network-mac=*)						;&
		--network-macv=*)						;&
		--network-macvl=*)						;&
		--network-macvla=*)						;&
		--network-macvlan=*)		_option_network_macvlan		;;
		--network-v|--network-v=*)					;&
		--network-ve|--network-ve=*)					;&
		--network-vet|--network-vet=*)	_error_option_is_ambiguous	;;
		--network-veth)			_option_network_veth		;;
		--network-veth=*)		_error_doesnt_allow_an_argument	;;
		--network-veth-)						;&
		--network-veth-e)						;&
		--network-veth-ex)						;&
		--network-veth-ext)						;&
		--network-veth-extr)						;&
		--network-veth-extra)		_error_requires_an_argument	;;
		--network-veth-=*)						;&
		--network-veth-e=*)						;&
		--network-veth-ex=*)						;&
		--network-veth-ext=*)						;&
		--network-veth-extr=*)						;&
		--network-veth-extra=*)		_option_network_veth_extra	;;
		--network-z|--network-zo)					;&
		--network-zon|--network-zone)	_error_requires_an_argument	;;
		--network-z=*|--network-zo=*)					;&
		--network-zon=*)						;&
		--network-zone=*)		_option_network_zone		;;
		--no|--not|--noti|--notif)					;&
		--notify|--notify-|--notify-r)					;&
		--notify-re|--notify-rea)					;&
		--notify-read|--notify-ready)	_error_requires_an_argument	;;
		--no=*|--not=*|--noti=*)					;&
		--notif=*|--notify=*)						;&
		--notify-=*|--notify-r=*)					;&
		--notify-re=*|--notify-rea=*)					;&
		--notify-read=*)						;&
		--notify-ready=*)		_option_notify_ready		;;
		--o|--o=*|--ov|--ov=*)						;&
		--ove|--ove=*|--over|--over=*)					;&
		--overl|--overl=*)						;&
		--overla|--overla=*)		_error_option_is_ambiguous	;;
		--overlay|--overlay-)						;&
		--overlay-r|overlay-ro)		_error_requires_an_argument	;;
		--overlay=*)			_option_overlay			;;
		--overlay-=*|--overlay-r=*)					;&
		--overlay-ro=*)			_option_overlay			;;
		--p|--p=*)			_error_option_is_ambiguous	;;
		--pe|--per|--pers|--perso)					;&
		--person|--persona|--personal)					;&
		--personali|--personalit)					;&
		--personality)			_error_requires_an_argument	;;
		--pe=*|--per=*|--pers=*)					;&
		--perso=*|--person=*)						;&
		--persona=*|--personal=*)					;&
		--personali=*|--personalit=*)					;&
		--personality=*)		_option_personality		;;
		--po|--por|--port)		_error_requires_an_argument	;;
		--po=*|--por=*|--port=*)	_option_port			;;
		--pr|--pr=*|--pri|--pri=*)					;&
		--priv|--priv=*)						;&
		--priva|--priva=*)						;&
		--privat|--privat=*)						;&
		--private|--private=*)						;&
		--private-|--private-=*)	_error_option_is_ambiguous	;;
		--private-n|--private-ne)					;&
		--private-net|--private-netw)					;&
		--private-netwo)						;&
		--private-networ)						;&
		--private-network)		_option_private_network		;;
		--private-n=*|--private-ne=*)					;&
		--private-net=*)						;&
		--private-netw=*)						;&
		--private-netwo=*)						;&
		--private-networ=*)						;&
		--private-network=*)		_error_doesnt_allow_an_argument	;;
		--private-u|--private-u=*)					;&
		--private-us|--private-us=*)					;&
		--private-use|--private-use=*)					;&
		--private-user)							;&
		--private-user=*)		_error_option_is_ambiguous	;;
		--private-users)						;&
		--private-users=*)						;&
		--private-users-)						;&
		--private-users-c)						;&
		--private-users-ch)						;&
		--private-users-cho)						;&
		--private-users-chow)						;&
		--private-users-chown)						;&
		--private-users-=*)						;&
		--private-users-c=*)						;&
		--private-users-ch=*)						;&
		--private-users-cho=*)						;&
		--private-users-chow=*)						;&
		--private-users-chown=*)	_option_private_users		;;
		--pro|--prop|--prope|--proper)					;&
		--propert|--property)		_error_requires_an_argument	;;
		--pro=*|--prop=*|--prope=*)					;&
		--proper=*|--propert=*)						;&
		--property=*)			_option_property		;;
		--q|--qu|--qui|--quie|--quiet)	_option_quiet			;;
		--q=*|--qu=*|--qui=*|--quie=*)					;&
		--quiet=*)			_error_doesnt_allow_an_argument	;;
		--r|--r=*|--re|--re=*)		_error_option_is_ambiguous	;;
		--rea|--read|--read-|--read-o)					;&
		--read-on|--read-onl)						;&
		--read-only)			_option_read_only		;;
		--rea=*|--read=*|--read-=*)					;&
		--read-o=*|--read-on=*)						;&
		--read-onl=*|--read-only=*)	_error_doesnt_allow_an_argument	;;
		--reg|--regi|--regis|--regist)					;&
		--registe|--register)		_error_requires_an_argument	;;
		--reg=*|--regi=*|--regis=*)					;&
		--regist=*|--registe=*)						;&
		--register=*)			_option_register		;;
		--s|--s=*|--se|--se=*)		_error_option_is_ambiguous	;;
		--sel|--sel=*|--seli|--seli=*)					;&
		--selin|--selin=*)						;&
		--selinu|--selinu=*)						;&
		--selinux|--selinux=*)						;&
		--selinux-|--selinux-=*)	_error_option_is_ambiguous	;;
		--selinux-a|--selinux-ap)					;&
		--selinux-api|--selinux-apif)					;&
		--selinux-apifs)						;&
		--selinux-apifs-)						;&
		--selinux-apifs-c)						;&
		--selinux-apifs-co)						;&
		--selinux-apifs-con)						;&
		--selinux-apifs-cont)						;&
		--selinux-apifs-contex)						;&
		--selinux-apifs-context)	_error_requires_an_argument	;;
		--selinux-a=*|--selinux-ap=*)					;&
		--selinux-api=*)						;&
		--selinux-apif=*)						;&
		--selinux-apifs=*)						;&
		--selinux-apifs-=*)						;&
		--selinux-apifs-c=*)						;&
		--selinux-apifs-co=*)						;&
		--selinux-apifs-con=*)						;&
		--selinux-apifs-cont=*)						;&
		--selinux-apifs-conte=*)					;&
		--selinux-apifs-contex=*)					;&
		--selinux-apifs-context=*)	_option_selinux_apifs_context	;;
		--selinux-c|--selinux-co)					;&
		--selinux-con|--selinux-cont)					;&
		--selinux-conte)						;&
		--selinux-contex)						;&
		--selinux-context)		_error_requires_an_argument	;;
		--selinux-c=*|--selinux-co=*)					;&
		--selinux-con=*)						;&
		--selinux-cont=*)						;&
		--selinux-conte=*)						;&
		--selinux-contex=*)						;&
		--selinux-context=*)		_option_selinux_context		;;
		--set|--set=*)			_error_option_is_ambiguous	;;
		--sete|--seten|--setenv)	_error_requires_an_argument	;&
		--sete=*|--seten=*|--setenv=*)	_option_setenv			;;
		--sett|--setti|--settin)					;&
		--setting|--settings)		_error_requires_an_argument	;;
		--sett=*|--setti=*|--settin=*)					;&
		--setting=*|--settings=*)	_option_settings		;;
		--sh|--sha|--shar|--share)					;&
		--share-|--share-s|--share-sy)					;&
		--share-sys|--share-syst)					;&
		--share-syste|--share-system)	_option_share_system		;;
		--sh=*|--sha=*|--shar=*)					;&
		--share=*|--share-=*)						;&
		--share-s=*|--share-sy=*)					;&
		--share-sys=*|--share-syst=*)					;&
		--share-syste=*)						;&
		--share-system=*)		_error_doesnt_allow_an_argument	;;
		--sl|--sli|--slic|--slice)	_error_requires_an_argument	;;
		--sl=*|--sli=*)							;&
		--slic=*--slice=*)		_option_slice			;;
		--t|--t=*)			_error_option_is_ambiguous	;;
		--te|--tem|--temp|--templ)					;&
		--templa|--templat|--template)	_error_requires_an_argument	;;
		--te=*|--tem=*|--temp=*)					;&
		--templ=*|--templa=*)						;&
		--templat=*|--template=*)	_option_template		;;
		--tm|--tmp|--tmpf|--tmpfs)	_error_requires_an_argument	;;
		--tm=*|--tmp=*)							;&
		--tmpf=*|--tmpfs=*)		_option_tmpfs			;;
		--u|--u=*)			_error_option_is_ambiguous	;;
		--us|--use|--user)		_error_requires_an_argument	;;
		--us=*|--use=*|--user=*)	_option_user			;;
		--uu|--uui|--uuid)		_error_requires_an_argument	;;
		--uu=*|--uui=*|--uuid=*)	_option_uuid			;;
		--v|--v=*)			_error_option_is_ambiguous	;;
		--ve|--ver|--vers|--versi)					;&
		--versio|--version)		_option_version			;;
		--ve=*|--ver=*|--vers=*)					;&
		--versi=*|--versio=*)						;&
		--version=*)			_error_doesnt_allow_an_argument	;;
		--vo|--vol|--vola|--volat)					;&
		--volati|--volatil|--volatile)	_error_requires_an_argument	;;
		--vo=*|--vol=*|--vola=*)					;&
		--volat=*|--volati=*)						;&
		--volatil=*|--volatile=*)	_option_volatile		;;
		--)								;;
		--*)				_error_unrecognized_option	;;
		-*a*)				_option_as_pid2			;;&
		-*b*)				_option_boot			;;&
		-*D)				_LAST_OPTION=directory		;;
		-*E)				_LAST_OPTION=setenv		;;
		-*h*)				_option_help			;;&
		-*i)				_LAST_OPTION=image		;;
		-*j*)				_option_link-journal		;;&
		-*L)	_USE_APIFS=true		_LAST_OPTION=selinux-context	;;
		-*M)				_LAST_OPTION=machine		;;
		-*n*)				_option_network-veth		;;&
		-*p)				_LAST_OPTION=port		;;
		-*q*)				_option_quiet			;;&
		-*S)				_LAST_OPTION=slice		;;
		-*U*)				_option_private_users		;;&
		-*u)				_LAST_OPTION=user		;;
		-*x*)				_option_ephemeral		;;&
		-*Z)	_USE_APIFS=false	_LAST_OPTION=selinux-context	;;
		-*[!abDEh-jLMnpqSUuxZ]*)	_error_invalid_option		;;
		-*)								;;
		*)	case $_LAST_OPTION		in
				command)		_USE_COMMAND=true
							break			;;
				directory)		_option_directory	;;
				image)			_option_image		;;
				machine)		_option_machine		;;
				port)			_option_port		;;
				selinux-context)	_option_selinux_context	;;
				setenv)			_option_setenv		;;
				slice)			_option_slice		;;
				user)			_option_user		;;
			esac							;;
	esac
done
[ -z $_USE_COMMAND ] && _USE_COMMAND=false
case $_USE_COMMAND	in
	true)		_SET_COMMAND=${_ALL_OPTIONS/$_REMOVE_OPTIONS/$_OPTION}	;;
	false)		_SET_COMMAND=bash\ \-l					;;
esac
unset _ALL_OPTIONS _LAST_OPTION _OPTION _REMOVE_OPTIONS _USE_COMMAND

# Need the "root" user to run this script
case $USER	in
	root)	;;
	*)
		echo Need to be root.
		exit 0
		;;
esac

#
[ -z $_USE_AS_PID2   ] &&   _USE_AS_PID2=false
[ -z $_USE_BOOT      ] &&      _USE_BOOT=false
[ -z $_USE_DIRECTORY ] && _USE_DIRECTORY=false
[ -z $_USE_EPHEMERAL ] && _USE_EPHEMERAL=false
[ -z $_USE_IMAGE     ] &&     _USE_IMAGE=false
[ -z $_USE_MACHINE   ] &&   _USE_MACHINE=false
[ -z $_USE_TEMPLATE  ] &&  _USE_TEMPLATE=false
if   [ $_USE_AS_PID2   = true  ] && [ $_USE_BOOT      = true  ]; then
	echo --boot= and --as-pid2= may not be combined.
	exit 1
elif [ $_USE_DIRECTORY = true  ] && [ $_USE_IMAGE     = true  ]; then
	echo --directory= and --image= may not be combined.
	exit 1
elif [ $_USE_DIRECTORY = false ] && [ $_USE_MACHINE   = false ] && [ $_USE_TEMPLATE = true ]; then
	echo --template= needs --directory= or --machine=.
	exit 1
elif [ $_USE_EPHEMERAL = true  ] && [ $_USE_TEMPLATE  = true  ]; then
	echo --ephemeral and --template= may not be combined.
	exit 1
elif [ $_USE_IMAGE     = true  ] && [ $_USE_TEMPLATE  = true  ]; then
	echo --template= and --image= may not be combined.
	exit 1
fi
unset _USE_DIRECTORY _USE_IMAGE _USE_MACHINE _USE_TEMPLATE

# Need the "_SET_DIRECTORY/bin" or the "_SET_DIRECTORY/usr/bin" like directory to run this script
if   [ ! -d $_SET_DIRECTORY/bin ]; then
	echo Directory $_SET_DIRECTORY doesn\'t look like it has OS tree. Refusing.
	exit 1
elif [ ! -d $_SET_DIRECTORY/usr/bin ]; then
	echo Directory $_SET_DIRECTORY doesn\'t look like it has OS tree. Refusing.
	exit 1
fi

# Set resolv.conf
if [ ! -d $_SET_DIRECTORY/etc			]; then
	[ -e $_SET_DIRECTORY/etc		] && rm		$_SET_DIRECTORY/etc
	mkdir	-m 0755	$_SET_DIRECTORY/etc
fi
[ -d $_SET_DIRECTORY/etc/resolv.conf		] && rmdir	$_SET_DIRECTORY/etc/resolv.conf
[ -e $_SET_DIRECTORY/etc/resolv.conf		] && rm		$_SET_DIRECTORY/etc/resolv.conf
cp	-L	/etc/resolv.conf	$_SET_DIRECTORY/etc

# Set the "_UNSHARE_NETWORK" variable if are enabled "_SET_PRIVATE_NETWORK" variable
[ -z _SET_PRIVATE_NETWORK ] && _SET_PRIVATE_NETWORK=false
case $_SET_PRIVATE_NETWORK	in
	true)			_UNSHARE_NETWORK=-n	;;
	false)			_UNSHARE_NETWORK=	;;
esac
unset _SET_PRIVATE_NETWORK

# Mount a fake "/" file system
mountpoint		-q		$_SET_DIRECTORY
[ $? != 0 ] && mount	-B		$_SET_DIRECTORY	$_SET_DIRECTORY
mount			--make-slave	$_SET_DIRECTORY

# Bind directories
_run_bind

# Run a virtual OS
[ -z $_USE_QUIET ] && _USE_QUIET=false
unshare -Cfimpu $_UNSHARE_NETWORK --mount-proc --setgroups allow -- chroot $_SET_DIRECTORY sh -c '{
	#
	case '$_USE_QUIET'	in
		true)		;;
		false)		echo -e Spawning container '${_SET_DIRECTORY##*/}' on '$_SET_DIRECTORY'"\n"Press ^] three times within 1s to kill container.
				;;
	esac

	# Create "/dev", "/proc", "/run", "/sys" and "/tmp" directories if are lost
	[ ! -d /dev				] && \
		mkdir	-m 0755	/dev
	[ ! -d /proc				] && \
		mkdir	-m 0555	/proc
	[ ! -d /run				] && \
		mkdir	-m 0755	/run
	[ ! -d /sys				] && \
		mkdir	-m 0555	/sys
	[ ! -d /tmp				] && \
		mkdir	-m 1777	/tmp

	# Mount "/dev", "/proc", "/run", "/sys" and "/tmp" file systems
	mountpoint	-q /dev
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o rw,nosuid,mode=755	\
		/dev
	mountpoint	-q /proc
	[ $? != 0 ] && mount	-t proc		proc	\
		-o rw,nosuid,nodev,noexec,relatime	\
		/proc
	mountpoint	-q /run
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o rw,nosuid,nodev,mode=755	\
		/run
	mountpoint	-q /sys
	[ $? != 0 ] && mount	-t sysfs	sysfs	\
		-o ro,nosuid,nodev,noexec,relatime	\
		/sys

	mountpoint	-q /tmp
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o rw	\
		/tmp

	# Create needed special files and directories
	[ ! -c /dev/console			] && \
		mknod	-m 0600	/dev/console	c 136	1
	[ ! -L /dev/core			] && \
		ln	-s	/proc/kcore			/dev/core
	[ ! -L /dev/fd				] && \
		ln	-s	/proc/self/fd			/dev/fd
	[ ! -c /dev/full			] && \
		mknod	-m 0666	/dev/full	c 1	7
	[ ! -d /dev/net				] && \
		mkdir	-m 0755	/dev/net
	[ ! -c /dev/null			] && \
		mknod	-m 0666	/dev/null	c 1	3
	[ ! -d /dev/pts				] && \
		mkdir	-m 0755	/dev/pts
	[ ! -c /dev/random			] && \
		mknod	-m 0666	/dev/random	c 1	8
	[ ! -d /dev/shm				] && \
		mkdir	-m 1777	/dev/shm
	[ ! -L /dev/stderr			] && \
		ln	-s	/proc/self/fd/2			/dev/stderr
	[ ! -L /dev/stdin			] && \
		ln	-s	/proc/self/fd/0			/dev/stdin
	[ ! -L /dev/stdout			] && \
		ln	-s	/proc/self/fd/1			/dev/stdout
	[ ! -c /dev/tty				] && \
		mknod	-m 0666	/dev/tty	c 5	0
	[ ! -c /dev/urandom			] && \
		mknod	-m 0666	/dev/urandom	c 1	9
	[ ! -c /dev/zero			] && \
		mknod	-m 0666	/dev/zero	c 1	5
#	[ ! -p /proc/kmsg			] && \
#		rm		/proc/kmsg
#		mknod	-m 0600	/proc/kmsg	p
	[ ! -d /proc/sys			] && \
		mkdir	-m 0555 /proc/sys
	[ ! -f /proc/sysrq-trigger		] && \
		touch		/proc/sysrq-trigger		&& \
		chmod	0400	/proc/sysrq-trigger
	[ ! -d /run/systemd			] && \
		mkdir	-m 0755	/run/systemd
	[ ! -d /run/systemd/nspawn		] && \
		mkdir	-m 0755	/run/systemd/nspawn
	[ ! -d /run/systemd/nspawn/incoming	] && \
		mkdir	-m 0500	/run/systemd/nspawn/incoming

	# Mount needed file systems
#	mountpoint	-q /dev/console
#	[ $? != 0 ] && mount	-t devpts	devpts	\
#		-o rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000	\
#		/dev/console
	mountpoint	-q /dev/pts
	[ $? != 0 ] && mount	-t devpts	devpts	\
		-o rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666	\
		/dev/pts
	mountpoint	-q /dev/shm
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o rw,nosuid,nodev	\
		/dev/shm
#	mountpoint	-q /proc/kmsg
#	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
#		-o rw,nosuid,nodev,mode=755	\
#		/proc/kmsg
#	mountpoint	-q /proc/sys
#	[ $? != 0 ] && mount	-t proc		proc	\
#		-o ro,nosuid,nodev,noexec,relatime	\
#		/proc/sys
#	mountpoint	-q /proc/sys/kernel/random/boot_id
#	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
#		-o ro,nosuid,nodev,mode=755	\
#		/proc/sys/kernel/random/boot_id
#	mountpoint	-q /proc/sys/kernel/random/boot_id
#	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
#		-o rw,nosuid,nodev,mode=755	\
#		/proc/sys/kernel/random/boot_id
#	mountpoint	-q /proc/sysrq-trigger
#	[ $? != 0 ] && mount	-t proc		proc	\
#		-o ro,nosuid,nodev,noexec,relatime	\
#		/proc/sysrq-trigger
	mountpoint	-q /run/systemd/nspawn/incoming
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o ro,relatime,mode=755	\
		/run/systemd/nspawn/incoming
	mountpoint	-q /sys/fs/cgroup
	[ $? != 0 ] && mount	-t tmpfs	tmpfs	\
		-o rw,nosuid,nodev,noexec,mode=755	\
		/sys/fs/cgroup

	# Create needed special files and directories from "/sys/fs/cgroup"
	[ ! -d /sys/fs/cgroup/blkio             ] && \
		mkdir   -m 0755 /sys/fs/cgroup/blkio
	[ ! -L /sys/fs/cgroup/cpu               ] && \
		ln	-s	cpu,cpuacct			/sys/fs/cgroup/cpu
	[ ! -L /sys/fs/cgroup/cpuacct           ] && \
		ln	-s	cpu,cpuacct			/sys/fs/cgroup/cpuacct
	[ ! -d /sys/fs/cgroup/cpu,cpuacct       ] && \
		mkdir   -m 0755 /sys/fs/cgroup/cpu,cpuacct
	[ ! -d /sys/fs/cgroup/cpuset            ] && \
		mkdir   -m 0555 /sys/fs/cgroup/cpuset
	[ ! -d /sys/fs/cgroup/devices           ] && \
		mkdir   -m 0755 /sys/fs/cgroup/devices
	[ ! -d /sys/fs/cgroup/freezer           ] && \
		mkdir   -m 0555 /sys/fs/cgroup/freezer
	[ ! -d /sys/fs/cgroup/memory            ] && \
		mkdir   -m 0755 /sys/fs/cgroup/memory
	[ ! -L /sys/fs/cgroup/net_cls           ] && \
		ln	-s	net_cls,net_prio		/sys/fs/cgroup/net_cls
	[ ! -d /sys/fs/cgroup/net_cls,net_prio  ] && \
		mkdir   -m 0555 /sys/fs/cgroup/net_cls,net_prio
	[ ! -L /sys/fs/cgroup/net_prio          ] && \
		ln	-s	net_cls,net_prio		/sys/fs/cgroup/net_prio
	[ ! -d /sys/fs/cgroup/pids              ] && \
		mkdir   -m 0755 /sys/fs/cgroup/pids
	[ ! -d /sys/fs/cgroup/systemd           ] && \
		mkdir   -m 0755 /sys/fs/cgroup/systemd

	# Mount needed file systems from "/sys/fs/cgroup"
	mountpoint	-q /sys/fs/cgroup
	[ $? = 0 ] && mount	\
		-o remount,ro	\
		/sys/fs/cgroup
	mountpoint	-q /sys/fs/cgroup/blkio
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,blkio	\
		/sys/fs/cgroup/blkio
#	mountpoint	-q /sys/fs/cgroup/cpu,cpuacct
#	[ $? != 0 ] && mount	-t cgroup	cgroup	\
#		-o ro,nosuid,nodev,noexec,relatime,cpu,cpuacct	\
#		/sys/fs/cgroup/cpu,cpuacct
	mountpoint	-q /sys/fs/cgroup/cpuset
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,cpuset	\
		/sys/fs/cgroup/cpuset
	mountpoint	-q /sys/fs/cgroup/devices
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,devices	\
		/sys/fs/cgroup/devices
	mountpoint	-q /sys/fs/cgroup/freezer
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,freezer	\
		/sys/fs/cgroup/freezer
	mountpoint	-q /sys/fs/cgroup/memory
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,memory	\
		/sys/fs/cgroup/memory
#	mountpoint	-q /sys/fs/cgroup/net_cls,net_prio
#	[ $? != 0 ] && mount	-t cgroup	cgroup	\
#		-o ro,nosuid,nodev,noexec,relatime,net_cls,net_prio	\
#		/sys/fs/cgroup/net_cls,net_prio
	mountpoint	-q /sys/fs/cgroup/pids
	[ $? != 0 ] && mount	-t cgroup	cgroup	\
		-o ro,nosuid,nodev,noexec,relatime,pids	\
		/sys/fs/cgroup/pids
#	mountpoint	-q /sys/fs/cgroup/systemd
#	[ $? != 0 ] && mount	-t cgroup	cgroup	\
#		-o rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd	\
#		/sys/fs/cgroup/systemd

	# Run an interactive shell or a custom command
	'"$_SET_COMMAND"'
}'
_ERROR_CODE=$?
unset _SET_COMMAND _UNSHARE_NETWORK

# Run commands before fail or exit the virtual OS
case $_ERROR_CODE	in
	0)		trap _trap_exit		EXIT INT QUIT TERM HUP	;;
	*)		trap _trap_error	EXIT INT QUIT TERM HUP	;;
esac
