537 lines
19 KiB
Bash
Executable File
537 lines
19 KiB
Bash
Executable File
#! /bin/bash -e
|
|
|
|
function error() {
|
|
echo "**** ERROR aborted. Status of last operation: $?" 1>&2
|
|
exit 1
|
|
}
|
|
trap error HUP INT QUIT TERM ERR
|
|
|
|
# log LEVEL "Text"
|
|
# LEVEL: 0=normal 1=verbose 2=more-verbose
|
|
function echon() {
|
|
if test "$1" == "-n"; then
|
|
shift
|
|
echo -n "$*"
|
|
else
|
|
echo "$*"
|
|
fi
|
|
}
|
|
function log() {
|
|
l=$1
|
|
shift
|
|
if test "$1" == "-n"; then
|
|
shift
|
|
n="-n"
|
|
else
|
|
n=""
|
|
fi
|
|
if test $q -eq 1; then
|
|
return
|
|
elif test $l -eq 0 -o $l -eq 1 -a $v -eq 1 -o $l -eq 2 -a $vv -eq 1; then
|
|
if test $l -gt 0; then
|
|
echon $n " $l→ $*"
|
|
else
|
|
echon $n "$*"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Arguments
|
|
################################################################################
|
|
q=0
|
|
v=0
|
|
vv=0
|
|
while test $# -gt 0; do
|
|
case "$1" in
|
|
(-q) q=1; v=0; vv=0;;
|
|
(-v) q=0; v=1; vv=0;;
|
|
(-vv) q=0; v=1; vv=1;;
|
|
(-h|--help)
|
|
echo "SYNOPSIS: $0 [-q|-v|-vv]"
|
|
echo " -q quiet"
|
|
echo " -v verbose"
|
|
echo " -vv more verbose"
|
|
exit 0;;
|
|
esac
|
|
shift
|
|
done
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# CONFIGURATION you can overwrite any of the variables when you start the script
|
|
# e.g. HW="x86_64 noarch" VERSION=11.4 sudo susebootstrap.sh
|
|
|
|
# Setup OS sources
|
|
#
|
|
# OS: name of the OS, used for chroot pathname
|
|
OS=${OS:-"opensuse"}
|
|
log 2 "OS=$OS"
|
|
#
|
|
# VERSION: see e.g. http://download.opensuse.org/distribution/
|
|
VERSION=${VERSION:-$(
|
|
case "$OS" in
|
|
opensuse) echo "openSUSE-current";;
|
|
fedora) echo "$(wget -O- http://mirror.switch.ch/ftp/mirror/fedora/linux/releases 2>/dev/null | html2 | sed -n 's,.*/a/@href=\([0-9][0-9]*\)/,\1,gp' | uniq | tail -1)";;
|
|
centos) echo "$(wget -O- http://mirror.switch.ch/ftp/mirror/centos 2>/dev/null | sed -n 's,.*href="\([0-9][0-9]*\)/".*,\1,pg' | uniq | tail -1)"
|
|
esac)}
|
|
log 2 "VERSION=$VERSION"
|
|
|
|
#
|
|
# HW: list of hardware subdirs in openSUSE, e.g. "x86_64 noarch"
|
|
HW=${HW:-$(
|
|
case "$OS" in
|
|
opensuse) echo "i586 noarch";;
|
|
fedora) echo "i686 noarch";;
|
|
centos) echo "x86_64 noarch";;
|
|
esac)}
|
|
log 2 "HW=$HW"
|
|
#
|
|
# BASEARCH: Base architecture, e.g. for i686 and i566 BASEARCH is i386
|
|
BASEARCH=${BASEARCH:-$(
|
|
case "${HW%% *}" in
|
|
i?86) echo "i386";;
|
|
*) echo "${HW%% *}";;
|
|
esac)}
|
|
log 2 "BASEARCH=$BASEARCH"
|
|
#
|
|
# URL: where to download packages from
|
|
URL=${URL:-$(
|
|
case "$OS" in
|
|
opensuse)
|
|
case "$VERSION" in
|
|
(tumbleweed) echo "http://download.opensuse.org/tumbleweed/repo/oss/suse";;
|
|
(*) echo "http://download.opensuse.org/distribution/${VERSION}/repo/oss/suse";;
|
|
esac;;
|
|
fedora)
|
|
if test "$VERSION" -gt 20; then
|
|
echo "http://mirror.switch.ch/ftp/mirror/fedora/linux/releases/${VERSION}/Server/${BASEARCH}/os"
|
|
else
|
|
echo "http://mirror.switch.ch/ftp/mirror/fedora/linux/releases/${VERSION}/Fedora/${BASEARCH}/os"
|
|
fi;;
|
|
centos) echo "http://mirror.switch.ch/ftp/mirror/centos/${VERSION}/os/${BASEARCH}";;
|
|
esac)}
|
|
log 2 "URL=$URL"
|
|
|
|
# Setup installation pathes
|
|
#
|
|
# BASE: Base path to the chroots on your system
|
|
BASE=${BASE:-/var/chroot}
|
|
log 2 "BASE=$BASE"
|
|
#
|
|
# SCHROOTCONF: Configurationfile for schroot
|
|
SCHROOTCONF=${SCHROOTCONF:-${OS}-${VERSION}_${BASEARCH}}
|
|
log 2 "SCHROOTCONF=$SCHROOTCONF"
|
|
#
|
|
# ROOT: Full path to the chroot directory
|
|
ROOT=${ROOT:-${BASE}/${SCHROOTCONF}}
|
|
log 2 "ROOT=$ROOT"
|
|
#
|
|
# PACKAGES: Where to store package lists path+filename-prefix
|
|
PACKAGES=${PACKAGES:-${ROOT}/var/tmp/packagelist}
|
|
log 2 "PACKAGES=$PACKAGES"
|
|
#
|
|
# PKGDIR: Where to store downloaded packages, relative to ${ROOT}
|
|
PKGDIR=${PKGDIR:-/var/tmp/packages}
|
|
log 2 "PKGDIR=$PKGDIR"
|
|
#
|
|
# SCHROOTUSER: Name of user or list of user that will use schroot
|
|
SCHROOTUSER=${SCHROOTUSER:-${USER}}
|
|
log 2 "SCHROOTUSER=$SCHROOTUSER"
|
|
#
|
|
# CHROOT: Command for starting chroot, more possibilities, see below
|
|
CHROOT="schroot -c ${SCHROOTCONF} -d / -u root"
|
|
log 2 "CHROOT=$CHROOT"
|
|
#
|
|
# YUM: Set Fedora / CentOS repository tool
|
|
if test "$OS" = "fedora" && test "${VERSION}" -gt "21"; then
|
|
YUM=${YUM:-"dnf"}
|
|
else
|
|
YUM=${YUM:-"yum"}
|
|
fi
|
|
log 2 "YUM=$YUM"
|
|
#
|
|
# PKGS: Base packages to install for minimal system
|
|
PKGS=${PKGS:-$(
|
|
case "$OS" in
|
|
opensuse) echo "bash rpm zypper";;
|
|
fedora)
|
|
echo "bash rpm fedora-release"
|
|
if test "${VERSION}" -gt "21"; then
|
|
echo "dnf"
|
|
else
|
|
echo "yum"
|
|
fi
|
|
;;
|
|
centos)
|
|
echo "bash rpm yum centos-release"
|
|
;;
|
|
esac)}
|
|
log 2 "PKGS=$PKGS"
|
|
#
|
|
#
|
|
if [ -z "${CHROOT}" ]; then
|
|
echo "**** ERROR: Variable CHROOT must be set." 1>&2
|
|
exit 1
|
|
fi
|
|
# ADDITIONAL_DEFINITIONS: Add additional definitions to /etc/schroot/chroot.d
|
|
# Example: ADDITIONAL_DEFINITIONS="setup.fstab=jenkins/fstab"
|
|
ADDITIONAL_DEFINITIONS=${ADDITIONAL_DEFINITIONS:-}
|
|
log 2 "ADDITIONAL_DEFINITIONS=$ADDITIONAL_DEFINITIONS"
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# Main Part
|
|
################################################################################
|
|
# 1. Download basic packages
|
|
# 2. Unpack them into a chroot-path
|
|
# 3. Tweak the minimal system
|
|
# 4. Setup basic /dev
|
|
# 5. Properly install all the packages within chroot
|
|
# 6. configure basic system
|
|
# 7. Done, now chroot to the path and install whatever you need
|
|
################################################################################
|
|
|
|
log 0 "Install ${OS} ${VERSION} for ${BASEARCH} in ${ROOT}"
|
|
|
|
################################################################################
|
|
# Preparation of path for chroot and packages
|
|
test -d ${ROOT}${PKGDIR} || mkdir -p ${ROOT}${PKGDIR}
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 0. Setup schroot configuration
|
|
cat > /etc/schroot/chroot.d/${SCHROOTCONF} <<EOF
|
|
[${SCHROOTCONF}]
|
|
description=${OS}-${VERSION} ${BASEARCH}
|
|
directory=${ROOT}
|
|
users=${SCHROOTUSER}
|
|
root-groups=root
|
|
root-users=${SCHROOTUSER}
|
|
type=directory
|
|
${ADDITIONAL_DEFINITIONS}
|
|
EOF
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 0. Search for dependencies to install
|
|
# requires: rpm xml2 schroot
|
|
log 0 " ... read repository meta data"
|
|
test -d ${PACKAGES} || mkdir -p ${PACKAGES}
|
|
test -f ${PACKAGES}/repomd.xml || \
|
|
wget -q -O ${PACKAGES}/repomd.xml ${URL}/repodata/repomd.xml
|
|
datapath=$(xml2 < ${PACKAGES}/repomd.xml \
|
|
| awk -F= 'BEGIN {x=0}
|
|
x==1 && $1=="/repomd/data/location/@href" {print $2; exit}
|
|
$1=="/repomd/data/@type" && $2=="primary" {x=1}')
|
|
dataname=${PACKAGES}/${datapath##*/}
|
|
log 2 "dataname=${dataname} from ${URL}/${datapath}"
|
|
log 0 " ... read repository details"
|
|
test -f ${dataname} || \
|
|
wget -q -O ${dataname} ${URL}/${datapath}
|
|
|
|
log 0 " ... recursively extract dependencies"
|
|
oldPKGS=""
|
|
if [ ! -f ${PACKAGES}/packages ]; then
|
|
test ! -f ${PACKAGES}/install || rm ${PACKAGES}/install
|
|
while [ "$PKGS" != "$oldPKGS" ]; do
|
|
oldPKGS=$PKGS
|
|
log 0 " ... extract dependencies: scan $(echo "$PKGS" | wc -w)" \
|
|
"packages"
|
|
log 0 " ... extract dependencies: find requirements"
|
|
PKGS=${PKGS% }
|
|
chkinstpkgs=$(echo $PKGS | sed -e 's/[^-A-Za-z0-9_ ]/./g' | tr ' ' '|')
|
|
deps=$(
|
|
(
|
|
echo "$PKGS" | tr ' ' '\n';
|
|
zcat $dataname | xml2 | awk -F= '
|
|
BEGIN {
|
|
x=0
|
|
}
|
|
x==1 &&
|
|
( $1=="/metadata/package/format/rpm:requires/rpm:entry/@name" ||
|
|
( $1=="/metadata/package/format/rpm:provides/rpm:entry/@name" &&
|
|
$2~/^('${chkinstpkgs}')$/ ) ) {
|
|
print $2
|
|
}
|
|
$1=="/metadata/package/name" && $2~/^('${chkinstpkgs}')$/ {
|
|
x=1
|
|
}
|
|
$1=="/metadata/package/name" && $2!~/^('${chkinstpkgs}')$/ {
|
|
x=0
|
|
}
|
|
$1=="/metadata/package/arch" && $2!~/^('${HW// /|}')$/ {
|
|
x=0
|
|
}'
|
|
) | \
|
|
sed -e 's/[^-A-Za-z0-9_]/./g' | \
|
|
sort | uniq | tr '\n ' '||' | \
|
|
sed -e 's/^|//' -e 's/|$//'
|
|
)
|
|
log 0 " ... extract dependencies: extend packagelist by providers"
|
|
PKGS=$(
|
|
zcat $dataname | xml2 | awk -F= '
|
|
BEGIN {
|
|
matcher="^('${deps}')$" #(|\\(.*\\))$"
|
|
}
|
|
$1=="/metadata/package/name" && $2~matcher {
|
|
name=$2
|
|
tmp=$2
|
|
gsub(/[^-A-Za-z0-9_]/, ".", tmp)
|
|
sub("\\|" tmp "\\|", "|", matcher)
|
|
sub("^" tmp ")\\|", "", matcher)
|
|
sub("\\|" tmp "$", "", matcher)
|
|
print name
|
|
}
|
|
$1=="/metadata/package/name" && $2!~matcher {
|
|
name=$2
|
|
}
|
|
( $1=="/metadata/package/format/rpm:provides/rpm:entry/@name" ||
|
|
$1=="/metadata/package/format/file" ) &&
|
|
$2~matcher {
|
|
tmp=$2
|
|
gsub(/[^-A-Za-z0-9_]/, ".", tmp)
|
|
sub("\\|" tmp "\\|", "|", matcher)
|
|
sub("^" tmp ")\\|", "", matcher)
|
|
sub("\\|" tmp "$", "", matcher)
|
|
print name
|
|
}
|
|
END {
|
|
}' | sort | uniq | tr '\n' ' '
|
|
)
|
|
((++pkgcnt)) ###
|
|
log 2 "temp packages: $PKGS"
|
|
log 2 "temp dependencies: $deps"
|
|
done
|
|
else
|
|
log 2 "packages already evaluated"
|
|
PKGS=$(<${PACKAGES}/packages)
|
|
fi
|
|
|
|
PKGS=${PKGS% }
|
|
echo ${PKGS} > ${PACKAGES}/packages
|
|
log 1 "final packages: $PKGS"
|
|
log 0 " ... extract dependencies: find files to download and install"
|
|
if [ -f ${PACKAGES}/install ]; then
|
|
log 2 "installations already evaluated"
|
|
INSTALL=$(<${PACKAGES}/install)
|
|
else
|
|
chkinstpkgs=$(echo $PKGS | sed -e 's/[^-A-Za-z0-9_ ]/./g' | tr ' ' '|')
|
|
INSTALL=$(
|
|
zcat $dataname | xml2 | awk -F= '
|
|
BEGIN {
|
|
x=0
|
|
}
|
|
x==1 && $1=="/metadata/package/location/@href" {
|
|
print $2
|
|
}
|
|
$1=="/metadata/package/name" && $2~/^('${chkinstpkgs}')$/ {
|
|
x=1
|
|
}
|
|
$1=="/metadata/package/name" && $2!~/^('${chkinstpkgs}')$/ {
|
|
x=0
|
|
}
|
|
$1=="/metadata/package/arch" && $2!~/^('${HW// /|}')$/ {
|
|
x=0
|
|
}' | sort | uniq | tr '\n' ' '
|
|
)
|
|
echo $INSTALL > ${PACKAGES}/install
|
|
fi
|
|
log 1 "installations: ${INSTALL}"
|
|
log 0 " ... download and install $(echo ${INSTALL} | wc -w) files"
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 1. Download basic packages
|
|
# We know the logical package names, find matching package files
|
|
# on server
|
|
# INST: Will be filled with a list of package files to download and
|
|
# install
|
|
|
|
for p in $INSTALL; do # for all packages to install
|
|
log 0 " ... download $p"
|
|
test -f ${ROOT}${PKGDIR}/${p##*/} || \
|
|
wget -q -O ${ROOT}${PKGDIR}/${p##*/} ${URL}/$p
|
|
INST="$INST ${p##*/}"
|
|
done
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 2. Unpack them into a chroot-path
|
|
# Extract all downloaded packages in the chroot directory
|
|
cd ${ROOT}
|
|
for p in $INST; do # for all downloaded packages
|
|
# just extract the file structure withing the package without running
|
|
# pre-/post-install scripts
|
|
# this is necessary for a minimal basic system to chroot in
|
|
log 0 " ... unpack $p"
|
|
rpm2cpio ${ROOT}${PKGDIR}/$p | cpio -dim --quiet 2> /dev/null
|
|
done
|
|
cd - > /dev/null
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 3. Tweak the minimal system
|
|
log 0 " ... setup system"
|
|
#
|
|
# Do some system setup tweaks
|
|
#
|
|
# Create minimal /etc/passwd and /etc/group with user "root"
|
|
test -e ${ROOT}/etc/passwd || \
|
|
echo 'root:x:0:0:root:/root:/bin/bash' > ${ROOT}/etc/passwd
|
|
test -e ${ROOT}/etc/group || \
|
|
( \
|
|
echo 'root:x:0:'; \
|
|
echo 'utmp:x:0:' \
|
|
) > ${ROOT}/etc/group
|
|
test -e ${ROOT}/etc/shadow || touch ${ROOT}/etc/shadow
|
|
test -e ${ROOT}/etc/gshadow || touch ${ROOT}/etc/gshadow
|
|
test -e ${ROOT}/etc/fstab || touch ${ROOT}/etc/fstab
|
|
#
|
|
# copy /etc/resolv.conf into chroot to be able to access internet
|
|
test -e ${ROOT}/etc/resolv.conf || \
|
|
cp /etc/resolv.conf ${ROOT}/etc/
|
|
|
|
################################################################################
|
|
# 4. Setup basic /dev
|
|
log 0 " ... setup prepare minimal /dev with /dev/null and /dev/zero"
|
|
if [ -n "${CHROOT//*schroot*/}" ]; then
|
|
# only if chroot is not schroot, schroot creates /dev
|
|
test -d ${ROOT}/dev || ${CHROOT} mkdir ${ROOT}/dev
|
|
test -e ${ROOT}/dev/null || \
|
|
( \
|
|
${CHROOT} mknod -m 666 /dev/null c 1 3 && \
|
|
${CHROOT} chown root:root /dev/null
|
|
)
|
|
test -e ${ROOT}/dev/zero || \
|
|
( \
|
|
${CHROOT} mknod -m 666 /dev/zero c 1 5 && \
|
|
${CHROOT} chown root:root /dev/zero \
|
|
)
|
|
fi
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 5. Properly install all the package packages within chroot
|
|
# now chroot into the new system and properly install all downloaded packages,
|
|
# this executes triggers and maintains package database
|
|
log 0 " ... install all packages"
|
|
all_success=1
|
|
if ! ${CHROOT} -- rpm -i --nodeps --force ${PKGDIR}/*.rpm > /dev/null 2> /dev/null; then
|
|
for p in ${ROOT}${PKGDIR}/*.rpm; do
|
|
log 0 -n " ... install ${p#${ROOT}}"
|
|
if ${CHROOT} -- rpm -i --nodeps --force ${p#${ROOT}} \
|
|
> /dev/null 2> /dev/null; then
|
|
log 0 " success"
|
|
else
|
|
all_success=0
|
|
log 0 " error"
|
|
fi
|
|
done
|
|
fi
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# System Dependent Part
|
|
################################################################################
|
|
case "$OS" in
|
|
(opensuse)
|
|
########################################################################
|
|
# 6. Add an archive source to package manager
|
|
# add installation source as repository
|
|
log 0 " ... setup package manager"
|
|
${CHROOT} -- zypper -q ar ${URL} repo-oss
|
|
#
|
|
# setup hardware architecture in zypper, necessary different from host
|
|
${CHROOT} -- perl -pi \
|
|
-e 's#^\#? *arch = .*$#arch = '${HW%% *}'#g' \
|
|
/etc/zypp/zypp.conf
|
|
#
|
|
# basic initialisation, first update - bug with key in 12.2
|
|
log 0 " ... basic initialisation of package manager"
|
|
${CHROOT} -- zypper -q -n --gpg-auto-import-keys update || \
|
|
${CHROOT} -- zypper -q -n --no-gpg-checks update
|
|
${CHROOT} -- zypper -q -n --gpg-auto-import-keys dist-upgrade || \
|
|
${CHROOT} -- zypper -q -n --no-gpg-checks dist-upgrade
|
|
#
|
|
# install some basic packages
|
|
log 0 " ... install more basic packages"
|
|
${CHROOT} -- zypper -q -n --gpg-auto-import-keys \
|
|
install openSUSE-release
|
|
########################################################################
|
|
;;
|
|
(fedora)
|
|
########################################################################
|
|
# 6. Install basic packages
|
|
log 0 " ... setup package manager using $YUM"
|
|
test -d ${ROOT}/etc/rpm || mkdir -p ${ROOT}/etc/rpm
|
|
sed -i 's,$basearch,'${BASEARCH}',g' \
|
|
$(grep -lr '$basearch' ${ROOT}/etc/yum.repos.d)
|
|
echo "${HW%% *}-${OS}-linux" > ${ROOT}/etc/rpm/platform
|
|
if test "${VERSION}" -gt "16"; then # hack for fedora 17 and above
|
|
log 0 " ... convert fedora 17+ filesystem"
|
|
${0%/*}/convertfs-fedora-17.sh ${ROOT}
|
|
fi
|
|
log 0 " ... upgrade base system"
|
|
${CHROOT} -- ${YUM} -y -q upgrade || \
|
|
( ${CHROOT} -- bash -c 'sed -i "s|^#baseurl|baseurl| ; s|^mirrorlist|#mirrorlist|" /etc/yum.repos.d/*' && \
|
|
${CHROOT} -- ${YUM} -y -q upgrade )
|
|
log 0 " ... install more basic packages"
|
|
for g in "Base" "Minimal Install"; do
|
|
if ${CHROOT} -- ${YUM} -y -q grouplist | grep -q "$g"; then
|
|
log 2 "install $g"
|
|
case "$VERSION" in
|
|
(*-Fedora)
|
|
${CHROOT} -- ${YUM} -y -q groups mark install "$g"
|
|
;;
|
|
(*)
|
|
${CHROOT} -- ${YUM} -y -q groupupdate "$g"
|
|
;;
|
|
esac
|
|
fi
|
|
done
|
|
########################################################################
|
|
;;
|
|
(centos)
|
|
########################################################################
|
|
# 6. Install basic packages
|
|
log 0 " ... setup package manager using $YUM"
|
|
${CHROOT} -- ln -s /sbin/chkconfig /usr/sbin/chkconfig
|
|
test -d ${ROOT}/etc/rpm || mkdir -p ${ROOT}/etc/rpm
|
|
echo "${HW%% *}-${OS}-linux" > ${ROOT}/etc/rpm/platform
|
|
if test "${VERSION}" -gt "6"; then # hack for fedora 17 and above
|
|
log 0 " ... convert fedora 17+ filesystem"
|
|
${0%/*}/convertfs-fedora-17.sh ${ROOT}
|
|
fi
|
|
log 0 " ... upgrade base system"
|
|
${CHROOT} -- ${YUM} -y -q upgrade || \
|
|
( ${CHROOT} -- bash -c 'sed -i "s|^#baseurl|baseurl| ; s|^mirrorlist|#mirrorlist|" /etc/yum.repos.d/*' && \
|
|
${CHROOT} -- ${YUM} -y -q upgrade )
|
|
log 0 " ... install more basic packages"
|
|
for g in "Base" "Minimal Install"; do
|
|
if ${CHROOT} -- ${YUM} -y -q grouplist | grep -q "$g"; then
|
|
log 2 "install $g"
|
|
${CHROOT} -- ${YUM} -y -q groups mark install "$g"
|
|
${CHROOT} -- ${YUM} -y -q upgrade
|
|
log 2 "update $g"
|
|
${CHROOT} -- ${YUM} -y -q groupupdate "$g"
|
|
fi
|
|
done
|
|
########################################################################
|
|
;;
|
|
esac
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# 7. Done - use your chroot to work or install more packages
|
|
log 0 "**** SUCCESS done."
|
|
log 0
|
|
log 0 "########################################################################"
|
|
log 0 "Use your new chroot:"
|
|
log 0 " > ${CHROOT} -- bash"
|
|
log 0 " > [... work in your chroot ...]"
|
|
log 0 " > exit"
|
|
log 0 "########################################################################"
|
|
log 0 " ... cleanup chroot"
|
|
################################################################################
|