mirror of
https://gitlab.alpinelinux.org/alpine/aports.git
synced 2026-01-04 16:21:38 +01:00
358 lines
7.8 KiB
Bash
358 lines
7.8 KiB
Bash
#!/bin/sh
|
|
|
|
# simple script to set up a new vserver guest
|
|
|
|
# test the first argument against the remaining ones, return success on a match
|
|
isin() {
|
|
local _a=$1 _b
|
|
|
|
shift
|
|
for _b; do
|
|
[ "$_a" = "$_b" ] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# remove all occurrences of first argument from list formed by
|
|
# the remaining arguments
|
|
rmel() {
|
|
local _a=$1 _b
|
|
|
|
shift
|
|
for _b; do
|
|
[ "$_a" != "$_b" ] && echo -n "$_b "
|
|
done
|
|
}
|
|
|
|
# Issue a read into the global variable $resp.
|
|
_ask() {
|
|
local _redo=0
|
|
|
|
read resp
|
|
case "$resp" in
|
|
!) echo "Type 'exit' to return to setup."
|
|
sh
|
|
_redo=1
|
|
;;
|
|
!*) eval "${resp#?}"
|
|
_redo=1
|
|
;;
|
|
esac
|
|
return $_redo
|
|
}
|
|
|
|
# Ask for user input.
|
|
#
|
|
# $1 = the question to ask the user
|
|
# $2 = the default answer
|
|
#
|
|
# Save the user input (or the default) in $resp.
|
|
#
|
|
# Allow the user to escape to shells ('!') or execute commands
|
|
# ('!foo') before entering the input.
|
|
ask() {
|
|
local _question=$1 _default=$2
|
|
|
|
while :; do
|
|
echo -n "$_question "
|
|
[ -z "$_default" ] || echo -n "[$_default] "
|
|
_ask && : ${resp:=$_default} && break
|
|
done
|
|
}
|
|
|
|
# Ask for user input until a non-empty reply is entered.
|
|
#
|
|
# $1 = the question to ask the user
|
|
# $2 = the default answer
|
|
#
|
|
# Save the user input (or the default) in $resp.
|
|
ask_until() {
|
|
resp=
|
|
while [ -z "$resp" ] ; do
|
|
ask "$1" "$2"
|
|
done
|
|
}
|
|
|
|
# Ask for the user to select one value from a list, or 'done'.
|
|
#
|
|
# $1 = name of the list items (disk, cd, etc.)
|
|
# $2 = question to ask
|
|
# $3 = list of valid choices
|
|
# $4 = default choice, if it is not specified use the first item in $3
|
|
#
|
|
# N.B.! $3 and $4 will be "expanded" using eval, so be sure to escape them
|
|
# if they contain spooky stuff
|
|
#
|
|
# At exit $resp holds selected item, or 'done'
|
|
ask_which() {
|
|
local _name=$1 _query=$2 _list=$3 _def=$4 _dynlist _dyndef
|
|
|
|
while :; do
|
|
# Put both lines in ask prompt, rather than use a
|
|
# separate 'echo' to ensure the entire question is
|
|
# re-ask'ed after a '!' or '!foo' shell escape.
|
|
eval "_dynlist=\"$_list\""
|
|
eval "_dyndef=\"$_def\""
|
|
|
|
# Clean away whitespace and determine the default
|
|
set -o noglob
|
|
set -- $_dyndef; _dyndef="$1"
|
|
set -- $_dynlist; _dynlist="$*"
|
|
set +o noglob
|
|
[ $# -lt 1 ] && resp=done && return
|
|
|
|
: ${_dyndef:=$1}
|
|
echo "Available ${_name}s are: $_dynlist."
|
|
echo -n "Which one $_query? (or 'done') "
|
|
[ -n "$_dyndef" ] && echo -n "[$_dyndef] "
|
|
_ask || continue
|
|
[ -z "$resp" ] && resp="$_dyndef"
|
|
|
|
# Quote $resp to prevent user from confusing isin() by
|
|
# entering something like 'a a'.
|
|
isin "$resp" $_dynlist done && break
|
|
echo "'$resp' is not a valid choice."
|
|
done
|
|
}
|
|
|
|
# Ask for user input until a non-empty reply is entered.
|
|
#
|
|
# $1 = the question to ask the user
|
|
# $2 = the default answer
|
|
#
|
|
# Save the user input (or the default) in $resp.
|
|
ask_until() {
|
|
resp=
|
|
while [ -z "$resp" ] ; do
|
|
ask "$1" "$2"
|
|
done
|
|
}
|
|
|
|
# http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
|
|
valid_hostname() {
|
|
# check length
|
|
if [ $(echo "$1" | wc -c) -gt 255 ]; then
|
|
echo "Hostname '$1' is too long."
|
|
return 1
|
|
fi
|
|
# check that it only contains valid chars
|
|
if ! [ -z "$(echo $1 | sed 's/[0-9.a-z-]//g')" ]; then
|
|
echo "Hostname must only contain letters (a-z), digits (0-9) or -"
|
|
return 1
|
|
fi
|
|
# must not start with -
|
|
case "$1" in
|
|
-*) echo "Hostname must not start with a '-'"; return 1;;
|
|
esac
|
|
return 0
|
|
}
|
|
|
|
# return last ipv4 address and mask
|
|
#
|
|
# $1 = interface
|
|
#
|
|
last_ipv4_addr_mask() {
|
|
local _iface=$1
|
|
ip addr show dev $_iface | sort | awk '$1 == "inet" {print $2}' | tail -n1
|
|
}
|
|
|
|
valid_ip_and_prefix() {
|
|
[ "$1" ] || return 0
|
|
ipcalc -s -m $1 >/dev/null 2>&1 && ! ipcalc -s -m $1/0 >/dev/null 2>&1
|
|
}
|
|
|
|
# ask for hostname
|
|
#
|
|
# $1 = default
|
|
#
|
|
# retrusn hostname in global var $resp
|
|
#
|
|
ask_hostname() {
|
|
while true; do
|
|
ask "Hostname for new vserver:" $1
|
|
if [ -d /etc/vservers/$resp ]; then
|
|
echo "/etc/vservers/$resp already exist"
|
|
continue
|
|
fi
|
|
if [ -d /vservers/$resp ]; then
|
|
echo "/vservers/$resp already exist"
|
|
continue
|
|
fi
|
|
valid_hostname $resp && break
|
|
done
|
|
}
|
|
|
|
available_ifaces() {
|
|
local iflist= ifpath= iface= i=
|
|
sorted_ifindexes=$(
|
|
for i in /sys/class/net/*/ifindex; do
|
|
[ -e "$i" ] || continue
|
|
echo -e "$(cat $i)\t$i";
|
|
done | sort -n | awk '{print $2}')
|
|
for i in $sorted_ifindexes; do
|
|
ifpath=${i%/*}
|
|
iface=${ifpath##*/}
|
|
# skip interfaces that are part of a bond or bridge
|
|
if [ -d "$ifpath"/master/bonding ] || [ -d "$ifpath"/brport ]; then
|
|
continue
|
|
fi
|
|
iflist="${iflist}${iflist:+ }$iface"
|
|
done
|
|
echo $iflist
|
|
}
|
|
|
|
ask_ifaceopts() {
|
|
# get ip address(es)
|
|
resp=
|
|
local ifaceopts= _def= _iface=
|
|
local ifaces=$(available_ifaces)
|
|
local last_iface=$(echo $ifaces | sed 's/.* //')
|
|
while [ "$resp" != "done" ]; do
|
|
if [ -z "$ifaces" ] || [ "$ifaces" = "lo " ] || [ -n "$ifaceopts" ]; then
|
|
_def="done"
|
|
else
|
|
_def=$(echo $ifaces | sed 's/.* //')
|
|
fi
|
|
ask_which "network interface" "to use for $_hostname" \
|
|
"$ifaces" $_def
|
|
[ "$resp" = "done" ] && break
|
|
|
|
_iface=$resp
|
|
ifaces=$(rmel $_iface $ifaces)
|
|
# suggested ip by last digit + 1
|
|
_last_ip_mask=$(last_ipv4_addr_mask $_iface)
|
|
_last_ip=${_last_ip_mask%/*}
|
|
_last_ip_digit=${_last_ip##*.}
|
|
_ip=${_last_ip%.*}.$((($_last_ip_digit + 1) % 256))
|
|
_mask=${_last_ip_mask#*/}
|
|
while true; do
|
|
ask "Enter IP address/mask for $_iface:" $_ip/$_mask
|
|
valid_ip_and_prefix "$resp" 2>&1 && break
|
|
echo "$resp is not a valid IPv4 address/mask"
|
|
done
|
|
_ip_mask=$resp
|
|
ifaceopts="$ifaceopts --interface $_iface:$_ip_mask"
|
|
|
|
# suggest context from last digit in first ip address
|
|
if [ -z "$_context" ]; then
|
|
_ip=${_ip_mask%/*}
|
|
_last_digit=${_ip##*.}
|
|
_context=$((10000 + $_last_digit))
|
|
fi
|
|
done
|
|
resp="$ifaceopts"
|
|
}
|
|
|
|
ask_context() {
|
|
# get context id
|
|
while true; do
|
|
ask "Enter context id for $_hostname:" $_context
|
|
if echo "$resp" | egrep -q "[0-9]+"; then
|
|
[ $resp -ge 0 ] && [ $resp -lt 65535 ] && break
|
|
fi
|
|
echo "Context id must be a 0-65534 number"
|
|
done
|
|
}
|
|
|
|
ask_guest_arch() {
|
|
# get guest arch
|
|
while true; do
|
|
ask "Enter guest machine architecture (i686 or x86_64):" $_arch
|
|
case "$resp" in
|
|
i[3-6]86|x86_64) break;;
|
|
esac
|
|
echo "Only i[3-6]86 and x86_64 is supported"
|
|
done
|
|
}
|
|
|
|
ask_template() {
|
|
local temp= apk_arch= arch_opt=
|
|
# get template
|
|
while true; do
|
|
ask "Enter template file (or empty for generate a new):" \
|
|
$_template
|
|
if [ -z "$resp" ] || [ -r "$resp" ]; then
|
|
break
|
|
fi
|
|
echo "Can not read $resp"
|
|
done
|
|
if [ "$_guest_arch" != "$_arch" ]; then
|
|
case "$_guest_arch" in
|
|
i?86) apk_arch=x86;;
|
|
*) apk_arch=$_guest_arch;;
|
|
esac
|
|
arch_opt="-a $apk_arch"
|
|
fi
|
|
temp=$resp
|
|
if [ -z "$temp" ]; then
|
|
temp=/vservers/template-$_guest_arch.tar.gz
|
|
echo "Generating template..."
|
|
if setup-vs-template $arch_opt -q -o $temp; then
|
|
echo "ok"
|
|
else
|
|
echo "Failed to create template"
|
|
exit 1
|
|
fi
|
|
fi
|
|
resp=$temp
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage: ${0##*/} [-h] [HOSTNAME...]"
|
|
exit 1
|
|
}
|
|
|
|
_arch="$(uname -m)"
|
|
|
|
while getopts "h" opt; do
|
|
case "$opt" in
|
|
h) usage;;
|
|
?) usage;;
|
|
esac
|
|
done
|
|
|
|
shift $(($OPTIND - 1))
|
|
|
|
if [ "$(whoami)" != "root" ]; then
|
|
echo "Need to be root. Sorry."
|
|
exit 1
|
|
fi
|
|
|
|
while true; do
|
|
ask_hostname $1
|
|
_hostname=$resp
|
|
|
|
ask_ifaceopts
|
|
_ifaceopts=$resp
|
|
|
|
ask_context
|
|
_context=$resp
|
|
|
|
_guest_arch=$_arch
|
|
if [ "$_arch" = "x86_64" ]; then
|
|
ask_guest_arch
|
|
_guest_arch=$resp
|
|
fi
|
|
|
|
ask_template
|
|
_template=$resp
|
|
|
|
vserver $_hostname build \
|
|
--hostname $_hostname \
|
|
$_ifaceopts \
|
|
--context $_context \
|
|
--initstyle openrc \
|
|
$arch_opt \
|
|
-m template -- -t "$_template" -d alpine \
|
|
&& cp /etc/resolv.conf /vservers/$_hostname/etc/ \
|
|
&& cp /etc/apk/repositories /vservers/$_hostname/etc/apk/ \
|
|
|| exit 1
|
|
if [ "$_arch" != "$_guest_arch" ]; then
|
|
echo "linux_32bit" >> /etc/vservers/$_hostname/personality
|
|
echo "$_guest_arch" > /etc/vservers/$_hostname/uts/machine
|
|
fi
|
|
|
|
shift
|
|
[ $# -le 0 ] && exit 0
|
|
done
|