#!/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