#!/bin/bash
#
# upd-instroot
#
# Copyright (C) 2007, 2008 Red Hat, Inc. All rights reserved.
#
# 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 2 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 .
#
ORIGDIR=`pwd`
DEBUG=""
ARCH=`uname -m`
while [ $# -gt 0 ]; do
case $1 in
--debug)
DEBUG="--debug"
shift
;;
--arch)
ARCH=$2
shift; shift
;;
--imgdir)
DEST=$2
shift; shift
;;
# a filesystem tree to use as updates. could be the output
# of 'make install' from anaconda...
--updates)
UPDATES=$2
shift; shift
;;
--nogr)
echo "*** DeprecationWarning: ignoring --nogr option." >&2
shift
;;
--mindir)
echo "*** DeprecationWarning: ignoring --mindir option." >&2
shift; shift
;;
--stg2dir)
echo "*** DeprecationWarning: please use --imgdir instead of --stg2dir." >&2
shift; shift
;;
*)
yumconf=$1
shift
;;
esac
done
if [ -z "$yumconf" ]; then
echo "upd-instroot: updates instimage from a Red Hat RPMS directory"
echo "usage: $0 [--debug] [--arch arch] [--imgdir imgdir] [yumconf]"
exit 1
fi
if [ $ARCH = x86_64 -o $ARCH = s390x -o $ARCH = ppc64 ]; then
LIBDIR=lib64
else
LIBDIR=lib
fi
if [ -z "$DEST" ]; then
DEST=`mktemp -d ${TMPDIR:-/tmp}/dest.XXXXXX`
fi
if [ ! -f $yumconf ]; then
echo "Unable to find yum repo information!"
exit 1
fi
. $(dirname $0)/buildinstall.functions
expandPackageSet() {
YUMCONF=$1
YUMDIR=$2
RPMS=$3
PKGDEST=$4
KEEPFILES=$5
[ -d $PKGDEST ] || die "ERROR: directory missing: $PKGDEST"
[ -z "$DEBUG" ] && outlvl="--quiet" || outlvl="--verbose"
yum $outlvl -c $YUMCONF -y --installroot=$YUMDIR install $RPMS 2>&1 || die "ERROR: could not install packages"
if [ -n "$UPDATES" ]; then
(cd $UPDATES; find) | (cd $UPDATES ; /bin/cpio --quiet -pmdu $YUMDIR)
fi
# figure out the theme to keep
if [ -f $YUMDIR/etc/gtk-2.0/gtkrc ]; then
gtktheme=$(grep "gtk-theme-name" $YUMDIR/etc/gtk-2.0/gtkrc | awk {'print $3;'} | sed -e 's/"//g')
echo "usr/share/themes/$gtktheme" >> $KEEPFILES
# find gtk engine needed
for engine in `grep engine $YUMDIR/usr/share/themes/$gtktheme/gtk-2.0/gtkrc | grep -v ^# | awk {'print $2;'} | sed -e 's/"//g' | sort -u` ; do
echo "usr/$LIBDIR/gtk-2.0/*/engines/*$engine*" >> $KEEPFILES
done
theme=$(grep "gtk-icon-theme-name" $YUMDIR/etc/gtk-2.0/gtkrc | awk {'print $3;'} | sed -e 's/"//g')
while [ -n "$theme" ]; do
echo "usr/share/icons/$theme" >> $KEEPFILES
theme=$(grep Inherits $YUMDIR/usr/share/icons/$theme/index.theme | cut -d = -f 2)
done
cursortheme=$(grep "gtk-cursor-theme-name" $YUMDIR/etc/gtk-2.0/gtkrc | awk {'print $3;'} | sed -e 's/"//g')
if [ -n "$cursortheme" ]; then
echo "usr/share/icons/$cursortheme" >> $KEEPFILES
fi
fi
echo `date` "Installing files"
pushd $YUMDIR >/dev/null
cat $KEEPFILES | while read spec ; do
#Pull off path
path=`echo "$spec" | sed 's,\([^[*\?]*\)/.*,\1,'`
for filespec in `find ./$path -path "./$spec" 2> /dev/null` ; do
if [ ! -e $filespec ]; then
continue
elif [ ! -d $filespec ]; then
instFile $filespec $PKGDEST
else
for i in `find $filespec -type f 2> /dev/null` ; do instFile $i $PKGDEST ; done
for i in `find $filespec -type l 2> /dev/null` ; do instFile $i $PKGDEST ; done
for d in `find $filespec -type d 2> /dev/null` ; do instDir $d $PKGDEST ; done
fi
done
done
popd >/dev/null
}
die () {
echo "$@"
echo "Aborting instroot creation..."
exit 1
}
setupSshd() {
echo "sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin" \
>> $DEST/etc/passwd
# enable root shell logins
echo "root::14438:0:99999:7:::" >> $DEST/etc/shadow
# enable 'install' account that starts anaconda on login
echo "install:x:0:0:root:/root:/sbin/loader" >> $DEST/etc/passwd
echo "install::14438:0:99999:7:::" >> $DEST/etc/shadow
chmod 0400 $DEST/etc/shadow
cat > $DEST/etc/pam.d/login << EOF
#%PAM-1.0
auth required pam_env.so
auth sufficient pam_unix.so likeauth nullok
auth required pam_deny.so
account required pam_unix.so
password sufficient pam_unix.so nullok use_authtok md5 shadow
password required pam_deny.so
session required pam_limits.so
session required pam_unix.so
EOF
cp -f $DEST/etc/pam.d/login $DEST/etc/pam.d/sshd
cp -f $DEST/etc/pam.d/login $DEST/etc/pam.d/remote
mkdir -m 0700 -p $DEST/etc/ssh
if [ "$ARCH" = "s390" -o "$ARCH" = "s390x" ]; then
# key generation takes ages on s390, you really don't want this
# for every installation attempt. These are NOT the keys of the
# installed system!
echo -n "Generating SSH1 RSA host key: "
/usr/bin/ssh-keygen -q -t rsa1 -f $DEST/etc/ssh/ssh_host_key \
-C '' -N '' >&/dev/null
echo
echo -n "Generating SSH2 RSA host key: "
/usr/bin/ssh-keygen -q -t rsa -f $DEST/etc/ssh/ssh_host_rsa_key \
-C '' -N '' >&/dev/null
echo
echo -n "Generating SSH2 DSA host key: "
/usr/bin/ssh-keygen -q -t dsa -f $DEST/etc/ssh/ssh_host_dsa_key \
-C '' -N '' >&/dev/null
echo
(cd $DEST/etc/ssh; \
chmod 600 ssh_host_key ssh_host_rsa_key ssh_host_dsa_key; \
chmod 644 ssh_host_key.pub ssh_host_rsa_key.pub ssh_host_dsa_key.pub; )
fi
cat > $DEST/etc/ssh/sshd_config.anaconda < $KEEPFILE <> $KEEPFILE <> $KEEPFILE <> $KEEPFILE < $KEEPFILERESCUE <> $KEEPFILERESCUE <<-EOF
sbin/grub
usr/bin/gpart
usr/share/grub
EOF
fi
echo "Assembling package list..."
RPMS="$PACKAGES $PACKAGESRESCUE"
[ -n "$DEBUG" ] && echo "RPMS are $RPMS"
rm -rf $DEST
# Create a bunch of empty directories that are important for a running system.
mkdir -p $DEST/boot
mkdir -p $DEST/dev
mkdir -p $DEST/etc/dhcp
mkdir -p $DEST/etc/modprobe.d
mkdir -p $DEST/firmware
mkdir -p $DEST/lib
mkdir -p $DEST/modules
mkdir -p $DEST/proc
mkdir -m 0700 $DEST/root
mkdir -p $DEST/selinux
mkdir -p $DEST/sys
mkdir -p $DEST/tmp
mkdir -p $DEST/usr/lib/rpm
mkdir -p $DEST/usr/sbin
mkdir -m 111 -p $DEST/var/empty/sshd
mkdir -p $DEST/var/lib/dbus
mkdir -p $DEST/var/lib/dhclient
mkdir -p $DEST/var/lib/rpm
mkdir -p $DEST/var/lock/rpm
mkdir -p $DEST/var/run
mkdir -p $DEST/var/run/dbus
mkdir -p $DEST/var/run/NetworkManager
mkdir -p $DEST/var/run/wpa_supplicant
mkdir -p $DEST/var/state
ln -s /tmp $DEST/var/lib/xkb
ln -s /tmp $DEST/var/state/xkb
#
# concat KEEPFILE and KEEPFILERESCUE lists
#
cat $KEEPFILERESCUE >> $KEEPFILE
echo `date` "Expanding packages..."
YUMDIR=${TMPDIR:-/tmp}/yumdir.$$
mkdir -p $YUMDIR/var/log
mkdir -p $YUMDIR/var/lib/yum
expandPackageSet $yumconf $YUMDIR "$RPMS" $DEST $KEEPFILE
echo `date` "Done Expanding packages..."
# Install the anaconda portions.
install -m 755 $DEST/usr/libexec/anaconda/auditd $DEST/sbin/auditd
install -m 755 $DEST/usr/$LIBDIR/anaconda/loader $DEST/sbin/loader
install -m 644 $DEST/usr/share/anaconda/loader.tr $DEST/etc/loader.tr
cp $DEST/usr/share/anaconda/raidstart-stub $DEST/usr/bin/raidstart
cp $DEST/usr/share/anaconda/raidstop-stub $DEST/usr/bin/raidstop
cp $DEST/usr/share/anaconda/list-harddrives-stub $DEST/usr/bin/list-harddrives
cp $DEST/usr/share/anaconda/loadkeys-stub $DEST/usr/bin/loadkeys
cp $DEST/usr/share/anaconda/restart-anaconda $DEST/usr/bin/restart-anaconda
mv $DEST/usr/$LIBDIR/python?.?/site-packages/pyanaconda/sitecustomize.py $DEST/usr/$LIBDIR/python?.?/site-packages
if [ $ARCH = "s390" -o $ARCH = "s390x" ]; then
mv $DEST/usr/libexec/anaconda/shutdown $DEST/sbin
mv $DEST/usr/share/anaconda/linuxrc.s390 $DEST/sbin/init
( cd $DEST/sbin && ln -sf init reboot && ln -sf init halt )
else
mv $DEST/usr/$LIBDIR/anaconda/init $DEST/sbin/init
( cd $DEST/sbin && ln -s init reboot && ln -s init halt && ln -s init poweroff )
install -m 644 $DEST/usr/share/anaconda/screenfont-$ARCH.gz $DEST/etc/screenfont.gz
fi
# Dogtail will check this
echo "Creating customized GConf2 settings for root"
mkdir -p $DEST/.gconf/desktop/gnome/interface
touch $DEST/.gconf/desktop/%gconf.xml
touch $DEST/.gconf/desktop/gnome/%gconf.xml
cat > $DEST/.gconf/desktop/gnome/interface/%gconf.xml <
EOF
# anaconda needs to change a couple of the default gconf entries
GCONF_CONFIG_SOURCE="xml:readwrite:$DEST/etc/gconf/gconf.xml.defaults"
echo "Updating gconf at $GCONF_CONFIG_SOURCE"
gconf_update() {
path=$1
entry_type=$2
value=$3
CMD="gconftool-2 --direct --config-source=$GCONF_CONFIG_SOURCE -s -t $entry_type $path $value"
$($CMD)
}
gconf_update /apps/metacity/general/button_layout string :
gconf_update /apps/metacity/general/action_right_click_titlebar string none
gconf_update /apps/metacity/window_keybindings/close string disabled
gconf_update /apps/metacity/global_keybindings/run_command_window_screenshot string disabled
gconf_update /apps/metacity/global_keybindings/run_command_screenshot string disabled
rm -rf $YUMDIR
chown -R root:root $DEST
chmod -R a+rX-w $DEST
# Some files need very particular permissions.
chmod 04755 $DEST/usr/libexec/polkit-1/polkit-agent-helper-1
chown root:dbus $DEST/$LIBDIR/dbus-1/dbus-daemon-launch-helper
chmod 04750 $DEST/$LIBDIR/dbus-1/dbus-daemon-launch-helper
# Generate default locale in case runtime doesn't work
# /usr/share/i18n/ contains sources
install -m 644 $DEST/usr/share/anaconda/lang-table $DEST/etc/lang-table
rm -f $DEST/usr/lib/locale/locale-archive
localedef -c -i en_US -f UTF-8 --prefix $DEST en_US
# Remove unsupported translations
cat $DEST/usr/share/anaconda/lang-table* | awk '
{ gsub("-", "", $4);
print $4;
print gensub(/\..*$/,"","",$4);
print gensub(/_.*$/,"","",$4);
if (split ($4, a, ".") > 1) {
print gensub(/\..*$/,tolower("." a[2]),"",$4);
};
print $2;
}
' | grep -v Sotho | grep -v latarcyrhebsun16 | sed -e 's/latn/Latn/g' | LC_ALL=C sort -u > $DEST/locales
for p in lib share; do (
cd $DEST/usr/$p/locale && {
ls | grep -v locale.alias | grep -v locale-archive | LC_ALL=C sort > $DEST/locales.list
LC_ALL=C comm -13 $DEST/locales $DEST/locales.list | xargs rm -rf
}
); done
rm -f $DEST/locales $DEST/locales.list
# fix up some links for man page related stuff
for file in nroff groff iconv geqn gtbl gpic grefer ; do
ln -fs /mnt/sysimage/usr/bin/$file $DEST/usr/bin/$file
done
# create selinux config
if [ -e $DEST/etc/selinux/targeted ]; then
cat > $DEST/etc/selinux/config < $DEST/etc/rsyslog.conf <<\EOF
#### MODULES ####
$ModLoad imuxsock.so # provides support for local system logging
$ModLoad imklog.so # provides kernel logging support
$ModLoad imfile
$InputFileName /tmp/X.log
$InputFileTag xserver:
$InputFileStateFile xserver-statefile
$InputFileFacility local1
$InputRunFileMonitor
$InputFileName /tmp/anaconda-tb-all.log
$InputFileTag anaconda-tb:
$InputFileStateFile anaconda-tb-statefile
$InputFileFacility local1
$InputRunFileMonitor
#### GLOBAL DIRECTIVES ####
# Use default timestamp format
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
#### TEMPLATES ####
$template anaconda_tty4, "%syslogseverity-text:::uppercase% %programname%:%msg%\n"
$template anaconda_syslog, "%timestamp:8:$:date-rfc3164%,%timestamp:1:3:date-subseconds% %syslogseverity-text:::uppercase% %programname%:%msg%\n"
$template virtio_ForwardFormat, "<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%\n"
#### RULES ####
# log everything except anaconda-specific records from local1 (those are stored
# directly into files via python logging)
*.*;\
authpriv.none;\
local1.none /tmp/syslog;anaconda_syslog
& /dev/tty4;anaconda_tty4
# ### begin forwarding rule ###
# The statement between the begin ... end define a SINGLE forwarding
# rule. They belong together, do NOT split them. If you create multiple
# forwarding rules, duplicate the whole block!
#
# An on-disk queue is created for this action. If the remote host is
# down, messages are spooled to disk and sent when it is up again.
$ActionQueueMaxDiskSpace 1m # space limit (use as much as possible)
$ActionQueueSaveOnShutdown off # do not save messages to disk on shutdown
$ActionQueueType LinkedList # run asynchronously
$ActionResumeRetryCount -1 # infinite retries if host is down
# remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
# ### end of the forwarding rule ###
EOF
cat > $DEST/etc/libuser.conf < $DEST/root/.profile < $DEST/root/.bash_history < /proc/sysrq-trigger
dmsetup table
multipath -d
HOME=/root chroot /mnt/sysimage bash -l -i
EOF
echo "Creating /etc/skel"
# libuser needs this when it creates sshpw users
mkdir -p $DEST/etc/skel
echo "Creating empty /etc/gshadow"
# libuser needs this when it creates sshpw users
touch $DEST/etc/gshadow
setupSshd
sed -i 's|\(installforallkernels\) = 0|\1 = 1|' $DEST/etc/yum/pluginconf.d/fedorakmod.conf
#
# Manual pages in rescue: We dont have man pages in the image, so we point everything (The pages
# and the man scripts to the /mnt/sysimage. We want the man command to depend only on the
# man_db.conf file, so we don't use the $MANPATH env variable. The executables stay unchanged as
# they will be soft links to /mnt/sysimage.
#
echo "Fixing up /etc/man_db.conf to point into /mnt/sysimage"
#
# Lets avoid the lines with MANPATH_MAP for now
#
sed -i "s,^MANPATH[^_MAP][ \t]*,&/mnt/sysimage," $DEST/etc/man_db.conf
#
# Lets change the lines with MANPATH_MAP. Don't know how much of a difference this will make.
#
sed -i "s,^MANPATH_MAP[ \t]*[a-zA-Z0-9/]*[ \t]*,&/mnt/sysimage," $DEST/etc/man_db.conf
echo "Scrubbing tree..." "$DEST"
mv $DEST/etc/yum.repos.d $DEST/etc/anaconda.repos.d
rm -f $DEST/usr/$LIBDIR/libunicode-lite*
rm -f $DEST/usr/share/icons/*/icon-theme.cache
rm -f $DEST/usr/sbin/build-locale-archive
find $DEST -type d | xargs chmod 755
cp $DEST/usr/share/doc/python-devel-*/gdbinit $DEST/.gdbinit
if [ -f $DEST/bin/bash ]; then
rm -f $DEST/bin/ash
ln -s bash $DEST/bin/sh
fi
if [ -f $DEST/bin/gawk ]; then
ln -sf $DEST/bin/gawk awk
fi
( cd $DEST/etc && ln -sf /etc/rc.d/init.d init.d )
ln -sf /sbin/init $DEST/init
ln -sf /proc/mounts $DEST/etc/mtab
# copy bootloader files for sparc
if [ $ARCH = sparc -o $ARCH = sparcv9 -o $ARCH = sparc64 ]; then
mkdir -p $DEST/usr/share/anaconda/boot
[ -d $DEST/boot ] || die "ERROR: directory missing: $DEST/boot"
(cd $DEST/boot; find -name "*.b") | (cd $DEST/boot; /bin/cpio --quiet -pdmu $DEST/usr/share/anaconda/boot)
fi
# copy bootloader file for ppc
if [ $ARCH = ppc -o $ARCH = ppc64 ]; then
mkdir -p $DEST/usr/share/anaconda/boot
cp -af $DEST/boot/efika.forth $DEST/usr/share/anaconda/boot
fi
# copy bootloader file for alpha
if [ $ARCH = alpha ]; then
mkdir -p $DEST/usr/share/anaconda/boot
cp -af $DEST/boot/bootlx $DEST/usr/share/anaconda/boot
fi
# copy bootloader files for ia64
if [ $ARCH = ia64 ]; then
mkdir -p $DEST/usr/share/anaconda/boot
cp -af $DEST/boot/efi/EFI/redhat//* $DEST/usr/share/anaconda/boot
fi
# copy bootloader files for i386/x86_64
if [ $ARCH = i386 -o $ARCH = i586 -o $ARCH = i686 -o $ARCH = x86_64 ]; then
mkdir -p $DEST/usr/share/anaconda/boot
cp -af $DEST/boot/memtest* $DEST/usr/share/anaconda/boot
fi
rm -rf $DEST/boot $DEST/home
# Remove a bunch of stuff we don't want in the final image.
find $DEST -name "*.a" | grep -v kernel-wrapper/wrapper.a | xargs rm -rf
find $DEST -name "lib*.la" |grep -v "usr/$LIBDIR/gtk-2.0" | xargs rm -rf
for i in $DEST/lib/udev/rules.d/*generator* ; do
[[ "$i" =~ net-generator ]] || rm -f $i
done
# nuke some python stuff we don't need
for d in idle distutils bsddb lib-old hotshot doctest.py pydoc.py site-packages/japanese site-packages/japanese.pth ; do
rm -rf $DEST/$d
done
$DEST/usr/libexec/anaconda/scrubtree $DEST
echo "Creating debug dir"
mkdir -p $DEST/usr/lib/debug
mkdir -p $DEST/usr/src/debug
find $DEST -name "*.py" | while read fn; do
rm -f ${fn}o
rm -f ${fn}c
ln -sf /dev/null ${fn}c
done
# some python stuff we don't need for install image
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/distutils/
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/lib-dynload/japanese
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/encodings/
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/compiler/
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/email/test/
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/curses/
rm -rf $DEST/usr/$LIBDIR/python?.?/site-packages/pydoc.py