#!/bin/bash #################################################################################### # Script to build an "INX is Not X" live CD. Uses debootstrap, casper, squashfs etc. # Copyright (C) 2008 Peter Garrett inx-one [ nospam at ] optusnet.com.au # Portions copyright (C) 2008 Karl Goetz karl@kgoetz.id.au # Acknowledgements to: Dan Mazzei, Karl Goetz # # 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. #################################################################################### #set -ex # debug, exit on error: un-comment only if you know how to fix things :) #set -vex # absurdly verbose debug. Lots of scrolling... See also above ;) # Colours, inevitably... white () { printf "\E[37m" ; } green () { printf "\E[32m" ; } yellow () { printf "\E[33m" ; } red () { printf "\E[31m" ; } bblack () { printf "\E[40m" ; } bold () { printf "\E[1m" ; } # The black terminal marks the script 'in action' bblack ; clear ; bold ; white if [ "$#" -ne 2 ] ; then cat << EOUSAGE buildinx: Usage: sudo $0 Examples: sudo $0 custom archive.ubuntu.com sudo $0 version2 au.archive.ubuntu.com EOUSAGE exit 1 elif [ $(id -u) -ne 0 ] ; then echo -e "\nYou must run this script as root." echo -e "Please run \"sudo "$0" \"" echo -e "Or become root before running "$0"\n" exit 1 elif ls -d /rofs > /dev/null 2>&1 ; then # See if there's a /rofs - if so, this is probably the live CD echo -e "\nYou appear to be running $0 from the live CD... You have a /rofs directory." echo -e "If so, this would be kind of pointless - try copying the script" echo -e "as root to your hard drive or something first, unless" echo -e "you have some clever ideas about it, or this is wrong :-)\n" echo -e "Go ahead anyway? (default is n for 'no')\n " read -s -n 1 GOAHEAD if [ "$GOAHEAD" = y -o "$GOAHEAD" = Y ] ; then : else exit fi fi green ; echo -e "\nBuildinx up and running...\n" ; sleep 1 ; white # Variables for version to build, repo mirror etc. export ARCH=i386 # We don't do amd64 etc... export NAME=$1 # First argument is desired name export DEBOOTSTRAP_LATEST=1.0.12 # As of writing: May 2009 - Edit as appropriate export DEV=karmic # Development version as at May 2009 - Edit as appropriate export SUITE=hardy # For this version, which is only for RC and final builds export MIRROR=$2 export MIRROR_SUBDIR= # Custom subdirs e.g. for ISP mirrors. Edit as required. # Note: watch the syntax: e.g. for mirror.internode.on.net/pub/ubuntu/ubuntu MIRROR_SUBDIR=pub/ubuntu export DISTRO=ubuntu # Might eventually work to sub debian, not tried. export DISTRO_L="$(lsb_release -si | tr 'A-Z' 'a-z')" #Convert to lower case for later comparison export MAIN=main # Using Debian terms - more generic. export CONTRIB=universe # Not an exact match for Debian contrib. Just a label... export NONFREE="multiverse" # Well, quite few of these are "libre" IMO export EXTRAS="" # For debian you would probably need at least "multimedia" export PACKAGEMIRROR="http://$MIRROR/${MIRROR_SUBDIR}$DISTRO" export INX_PPA="deb http://ppa.launchpad.net/inx-devel/ubuntu hardy main" export DEBOOTSTRAP_PACKAGE=debootstrap_"$DEBOOTSTRAP_LATEST"_all.deb export WORK_DIR="$1"-inx export LC_ALL=C export INX_SITE=inx.maincontent.net export INX_SITE_ALT=thoreauputic.boinc.ch export INX_DIR=buildinx export INX_EXTRAS="inx-extras" export SUITE_SCRIPTS=scripts.tar.gz # Removed suite options - hardy only for this version # Make a general directory to contain all the Buildinx cruft ;-) if ! [ -d BuildInxWorkingDirectory ] ; then mkdir BuildInxWorkingDirectory ; fi cd BuildInxWorkingDirectory if [ ! -f .inx_tools_check ] ; then # To avoid constant checking on re-runs. green ; echo -e "\nRoutine check for needed tools...\n" sleep 3 if ! which apt-get ; then yellow ; echo -e "\nAaaargh! No apt-get!! Exiting.\n" ; white exit 1 fi # Let apt do the work for us here... white if ! apt-get install squashfs-tools mkisofs initramfs-tools wget binutils netcat ; then # Fail gracefully yellow ; echo -e "\nApt was unable to install all tools needed to continue. Exiting...\n" ; white exit 1 fi fi touch .inx_tools_check if ! [ -d "$INX_EXTRAS" ] ; then green ; echo -e "\nChecking site $INX_SITE for needed INX extras...\n" ; sleep 3 ; white if ! nc -w 2 -z $INX_SITE 80 ; then green ; echo -e "\n$INX_SITE seems to be down ... trying an alternative ...\n" ; sleep 3 ; white if nc -w 2 -z $INX_SITE_ALT 80 ; then INX_SITE=$INX_SITE_ALT else yellow ; echo -e "\nNo luck... $INX_SITE_ALT is down too! Please try again later. Exiting.\n" ; white exit 1 fi fi # Get the tarball of needed extra INX files etc. This only happens once... wget --continue -T 5 "$INX_SITE"/"$INX_DIR"/buildinx-extras.tar \ && tar xvf buildinx-extras.tar && rm buildinx-extras.tar if [ $? -ne 0 ] ; then yellow ; echo -e "\nThere was a problem obtaining the needed extras... Exiting.\n" ; white exit 1 fi fi # Check on-line status if ! nc -w 5 -z $MIRROR 80 ; then # Allowing more time - if the server is being hammered... yellow echo -e "\nThe mirror address "$MIRROR" appears to be down, or not responding." echo -e "Try another mirror, check for errors, or re-run $0 later ... Exiting.\n" white exit 1 fi warning () { if ! [ "$DISTRO" = "$DISTRO_L" ] ; then # Check that we are on $DISTRO yellow echo -e "\nThis script has only been tested on an Ubuntu system." echo -e "*Running* the script *probably* won't work on Debian, or other Debian derivatives.(Not tested)" echo -e "To *build* an INX-Debian or other derivative, you would need to change the script." echo -e "Saying \"n\" for no here is the safe alternative, if you aren't sure..." echo -e "Go ahead and try anyway? [y/n]\n" white other () { read -s -n 1 GO case $GO in n | N) echo -e "OK -exiting now.\n" exit 0 ;; y | Y) echo -e "O.K. - Good luck!\n" ; sleep 3 ;; *) echo "We really need yes or no here :)" other ;; esac } other fi # Check debootstrap version DEBOOTSTRAP_VERSION=$(debootstrap --version | awk '{print $2}') if [ "$DEBOOTSTRAP_VERSION" != "$DEBOOTSTRAP_LATEST" ] ; then green echo -e "\nYou need to install the most recent version of debootstrap:" echo -e "downloading & installing... Please wait.\n" white sleep 3 wget --continue -T 5 $PACKAGEMIRROR/pool/main/d/debootstrap/$DEBOOTSTRAP_PACKAGE dpkg -i $DEBOOTSTRAP_PACKAGE # Shell script - only needs binutils. Deb only for now. if [ $? -ne 0 ] ; then yellow ; echo -e "\nOops! Unable to install the latest debootstrap... Exiting.\n" ; white exit 1 fi fi } debootstrapping () { if ! [ -d "$WORK_DIR" ] ; then mkdir -p "$WORK_DIR"/{chroot,master} cd "$WORK_DIR" # Put isolinux, netinxtaller and casper dirs in place, for the INX start screen, boot etc. tar -C master/ -xvzf ../"$INX_EXTRAS"/isolinux.tar.gz tar -C master/ -xvzf ../"$INX_EXTRAS"/netinxtaller.tar.gz mkdir master/casper # Do the debootstrap in $WORK_DIR green echo -e "\nDownloading and installing a minimal bootstrap chroot. Please wait.\n" sleep 3 white if ! debootstrap --arch=$ARCH $SUITE ./chroot $PACKAGEMIRROR ; then echo -e "\nDebootstrap failed... Cleaning up... exiting.\n" # Clean up cd .. ; rm -rf "$WORK_DIR" exit 1 fi # Mount, edit ./chroot/etc/apt/sources.list, update apt list chroot ./chroot mount -t proc proc /proc chroot ./chroot mount -t sysfs sys /sys chroot ./chroot mount -t devpts devpts /dev/pts chroot ./chroot apt-get clean # Quoting variables in a "Here" doc doesn't work - reverting for now. echo "# Autogenerated by $0 " > ./chroot/etc/apt/sources.list echo "" >> ./chroot/etc/apt/sources.list echo "deb $PACKAGEMIRROR $SUITE $MAIN $CONTRIB $NONFREE" >> ./chroot/etc/apt/sources.list echo "deb $PACKAGEMIRROR "$SUITE"-updates $MAIN $CONTRIB $NONFREE" >> ./chroot/etc/apt/sources.list echo "deb $PACKAGEMIRROR "$SUITE"-security $MAIN $CONTRIB $NONFREE" >> ./chroot/etc/apt/sources.list # INX PPA on Launchpad echo "$INX_PPA" >> ./chroot/etc/apt/sources.list # Install extra repositories if enabled if [ $EXTRAS ]; then echo "deb $PACKAGEMIRROR $EXTRAS" >> ./chroot/etc/apt/sources.list ; fi # Grab the inx PPA key... if ! [ -f .ppa_keys ] ; then echo -e "\nAdding gpg key for the INX Personal Package Archive...\n" sleep 3 chroot ./chroot apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 89449B3E touch .ppa_keys fi echo -e "\nUpdating package repositories data...\n" sleep 3 if ! chroot ./chroot apt-get update ; then echo -e "\nApt-get update in the chroot failed. Please check your mirror and network. Exiting...\n" exit 1 fi if [ $SUITE = hardy ] ; then # Makes setting LANG easier. Whiptail is not installed early enough. chroot ./chroot apt-get install dialog fi # Grab and install the lot. chroot ./chroot dpkg --set-selections < ../"$INX_EXTRAS"/selections yellow echo -e "\nThe next step is to retrieve and install necessary packages." echo -e "This requires 100 MB or so of downloading, and could take a while.\n" echo -e "You may be asked about kernel symlinks and locale. Say yes to the symlinks," echo -e "and select only 'en' for the locale question, unless you want to experiment...\n" echo -e "Scroll down with arrow keys, select with space bar.\n" echo -e "The kernel configuration questions may ask if you \"Want to abort\" . Say no." echo -e "You probably want to leave the time zone as \"unknown\"\n" green echo -e "\nHit any key to continue. Apt will show a long list, then proceed to download and install." read -s -n 1 white if ! chroot ./chroot apt-get --no-install-recommends dselect-upgrade ; then # Try to fix unconfigured packages - e.g. vifm causes some issues with hardy. chroot ./chroot aptitude -f install # 'apt-get -f install' seems to fail here, though it isn't fatal. chroot ./chroot dpkg --configure --pending # Never rely on the frontend to do a backends job :) chroot ./chroot apt-get clean # We don't want cache bloating the final ISO else chroot ./chroot apt-get clean fi else cd "$WORK_DIR" fi } script_install () { if ! [ -f .scripts_installed ] ; then bold ; green ; echo -e "\nInstalling INX scripts in chroot/usr/local...\n" ; white ; sleep 4 tar -C chroot/usr/ -xvzf ../"$INX_EXTRAS"/"$SUITE_SCRIPTS" touch .scripts_installed # Run mandb -c in the chroot to update local man pages like "man inx" etc... echo -e "\nUpdating manual page database, please wait...\n" sleep 3 chroot ./chroot mandb -c fi } # This is where the fun starts... we get here if the working $NAME-inx dir has been set up. ##debug### Rewrite this, and use at crucial points in the flow. P.G. choices () { bblack ; clear ; green ; bold echo -e "\nIf you are seeing this, you should have a working debootstrapped chroot." echo -e "Here you can choose what to do next...\n" ; yellow echo -e "Select by number... \"1\" is suggested, if you have no idea: - or just panic ;-)" ; green echo -e "1: Let the script complete a clone of INX for you.\n" ; yellow echo -e "Advanced:" ; green echo "2: Enter the chroot to customise your INX file system. (add applications, edit configuration etc)." ; yellow echo -e "\nUltra Advanced :-) " ; red echo "3: Panic!" ; green echo -e "\n4: Exit buildinx. (You will need to know what to do next...)\n" echo -e "\n5: Install a different or updated set of scripts for /usr/local (new tarball).\n" ; white read -s -n 1 CHOICES case $CHOICES in 1) : # No op... next in "main ()" is "add_inx_examples ()" ;; 2) green ; echo -e "\nWhen you exit the chroot, you will come back to this menu." ; sleep 2 tput sgr0 ; clear HOME=/root chroot ./chroot choices ;; 3) red ; echo -e "\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARRRGH!!!!!!!!\n" sleep 2 choices ;; 4) echo -e "\nOK - exiting now...\n" ; sleep 2 if ! chroot ./chroot umount /proc /sys /dev/pts > /dev/null 2>&1 ; then # This is a bit brutal. chroot ./chroot umount -l /proc /sys /dev/pts > /dev/null 2>&1 fi reset_and_exit ;; 5) green ; bold echo -e "\nOK - reinstalling scripts in /usr/local of the chroot as requested...\n" sleep 3 rm .scripts_installed script_install choices ;; *) echo -e "\nTry again... the idea is to *bang the rocks together*... ;-)" sleep 3 choices ;; esac } add_inx_examples () { # Check if example directories already exist from previous runs (do this bit, if not) if ! ls -ld chroot/usr/share/{video,graphics,example-content} ; then tar -C chroot/usr/share/ -xvzf ../"$INX_EXTRAS"/graphics.tar.gz tar -C chroot/usr/share/ -xvzf ../"$INX_EXTRAS"/video.tar.gz tar -C chroot/usr/share/ -xvzf ../"$INX_EXTRAS"/example-content.tar.gz if [ $? -ne 0 ] ; then echo -e "\nOops... exiting\n" ; exit 1 ; fi else bold ; green echo -e "\nYou seem to have the INX example directories set up already:" echo -e " moving on to the next step.\n" white sleep 4 fi } # Add scripts and tweak settings (/etc/skel , start-up hacks ... ) configure_inx () { bold ; green echo -e "\nTweaking system settings..." ; sleep 2 white if ! [ -f .etc.skel.tweaks ] ; then # If no marker file, do this... green echo -e "\nInstalling custom /etc/skel settings...\n" ; sleep 3 white tar -C chroot/etc/ -xvzf ../"$INX_EXTRAS"/etc.skel.tar.gz touch .etc.skel.tweaks fi # These are the really fiddly bits :/ # The usbmount config needs additions to recognise and handle FAT usb file systems... if ! [ -f .usb ] ; then green ; echo -e "\nConfiguring usbmount...\n" ; white ; sleep 2 sed -i 's/FILESYSTEMS="ext2 ext3"/FILESYSTEMS="ext2 ext3 vfat"/g' \ ./chroot/etc/usbmount/usbmount.conf sed -i 's/FS_MOUNTOPTIONS=""/FS_MOUNTOPTIONS="-fstype=vfat,gid=floppy,dmask=0007,fmask=0117"/g' \ ./chroot/etc/usbmount/usbmount.conf touch .usb fi if ! [ -f .general_tweaks ] ; then green ; echo -e "General tweaks for themes, media playing, etcetera..." ; sleep 3 # Add greengold theme for MOC cp ../"$INX_EXTRAS"/moc-theme-inx ./chroot/usr/share/moc/themes/greengold # Green theme for irssi... mv chroot/usr/share/irssi/themes/default.theme chroot/usr/share/irssi/themes/blue.theme cp ../"$INX_EXTRAS"/default.theme chroot/usr/share/irssi/themes/default.theme # Switch mplayer to fbdev video, specify full screen by default (fbdev and fs are better in INX) sed -i 's/vo=xv/vo=fbdev/g' chroot/etc/mplayer/mplayer.conf sed -i 's/#fs=yes/fs=yes/g' chroot/etc/mplayer/mplayer.conf # Add "toram" option in casper, and shutdown tweak for case of "toram" (i.e. don't eject CD if "toram") cp ../"$INX_EXTRAS"/casper ./chroot/usr/share/initramfs-tools/scripts/casper cp ../"$INX_EXTRAS"/S89casper ./chroot/etc/rc0.d/S89casper # Comment out ogg123 line in /etc/mailcap - ogg is not just audio! sed -i 's_application/ogg_#application/ogg_g' chroot/etc/mailcap echo "application/ogg; playinx '%s'; test=test -z \"\$DISPLAY\"; needsterminal" >> chroot/etc/mailcap echo "application/x-ogg; playinx '%s'; test=test -z \"\$DISPLAY\"; needsterminal" >> chroot/etc/mailcap touch .general_tweaks fi # Removed the suid on links2 and added devices to "video" group : run with "links2-watch" wrapper P.G. 2008-08-22 if ! [ -f .permissions_tweaks ] ; then # Doesn't work on jaunty echo '' >> ./chroot/etc/udev/rules.d/40-permissions.rules echo 'KERNEL=="mice" GROUP="video", MODE="0660"' >> ./chroot/etc/udev/rules.d/40-permissions.rules echo 'KERNEL=="tty[0-9]*" GROUP="video", MODE="0660"' >> ./chroot/etc/udev/rules.d/40-permissions.rules echo 'KERNEL=="fb0" GROUP="video", MODE="0660"' >> ./chroot/etc/udev/rules.d/40-permissions.rules echo '' >> ./chroot/etc/udev/rules.d/40-permissions.rules cat << EOF >> ./chroot/etc/init.d/bootmisc.sh chown root:video /dev/fb0 /dev/tty0 /dev/input/mice chmod 660 /dev/fb0 /dev/tty0 /dev/input/mice : EOF touch .permissions_tweaks fi # The following are idempotent. [ I've always wanted to use that word in a comment. ;-) ] P.G. # Fix the xlinks2 wrapper - the 'exec' is left out (known unfixed bug) # This probably *ought* to be done with dpkg-divert or something... Also maybe try full path. Note to self P.G. echo '#!/bin/sh' > chroot/usr/bin/xlinks2 echo 'exec links2 -g "$@"' >> chroot/usr/bin/xlinks2 # Set the user and system name to "inx@inx". sed -i 's/ubuntu/inx/g' chroot/etc/casper.conf # Remove contents of these files, which are misleading for inx. Also makes starts a fraction quicker ;) > chroot/etc/motd > chroot/etc/motd.tail # Editor and browser defaults.. chroot ./chroot update-alternatives --set editor /usr/bin/jed chroot ./chroot update-alternatives --set www-browser /usr/bin/elinks } make_inx () { yellow # If this is a re-run, there might have been a kernel upgrade. We don't *allow* more than one kernel... ;) if [ $(chroot ./chroot dpkg -l linux-image-2* | grep -c ^ii) -gt 1 ] ; then echo -e "\nYou appear to have more than one chroot kernel installed. This confuses matters, and takes CD space." echo -e "Please enter the chroot and remove redundant kernels with \"aptitude purge \"..." echo -e "Hit any key to return to the menu.\n" read -s -n 1 choices fi green echo -e "\nPutting the kernel files in place, and generating the compressed file system." echo -e "This will take some minutes, and show a *lot* of output on the screen - please wait...\n" sleep 3 # Do the mkinitramfs in chroot to pick up casper config changes etc. echo -e "Regenerating initrd, and configuring initrd and kernel for isolinux/casper." echo -e "Nothing much will appear to happen for a moment or so, then all hell breaks loose ;-)\n" sleep 4 LINUX_VERSION=$(ls ./chroot/boot/vmlinuz* | cut -d'-' -f1 --complement) chroot ./chroot mkinitramfs -o boot/initrd.gz $LINUX_VERSION # Get rid of any backup initrd image in chroot/boot - it just takes space, and does nothing. rm chroot/boot/initrd*.bak > /dev/null 2>&1 # Copy the initrd and vmlinuz across. # Any extra vmlinuz* in chroot/boot has already been nuked, hopefully... mv chroot/boot/initrd.gz master/casper/initrd.gz cp chroot/boot/vmlinuz* master/casper/vmlinuz # Make /etc/issue clear the screen and print the name... clear > chroot/etc/issue echo "INX $NAME \n \l" >> chroot/etc/issue echo "INX $NAME" > chroot/etc/issue.net # Check for existing filesystem.squashfs: - remove it, or everything goes pear-shaped. if [ -f master/casper/filesystem.squashfs ] ; then rm master/casper/filesystem.squashfs fi # Make *sure* nothing is mounted in the chroot, before making the squashfs! if ! chroot ./chroot umount /proc /sys /dev/pts > /dev/null 2>&1 ; then # This is a bit brutal. Once is not enough, apparently. There's probably a better way ;) chroot ./chroot umount -l /proc /sys /dev/pts > /dev/null 2>&1 fi # OK, time to rock and roll... yellow ; echo -e "\nHold on to your hat... here we go!" green ; echo -e "Building compressed file system. Please wait...\n" ; sleep 2 white mksquashfs chroot master/casper/filesystem.squashfs -info # Check for existing ISO from previous run... if [ -f inx-"$NAME".iso ] ; then yellow echo -e "\nYou have a file inx-"$NAME".iso already." echo -e "Moving it to $(date +%y%m%d-%H.%M).inx-"$NAME".iso as a time-stamped version." green mv inx-"$NAME".iso $(date +%y%m%d-%H.%M).inx-"$NAME".iso sleep 5 fi green ; echo -e "\nMaking the ISO image inx-"$NAME".iso - please wait...\n" white ; sleep 4 mkisofs -l -r -J -V "inx-"$NAME"" -hide-rr-moved -v \ -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 \ -boot-info-table -o inx-"$NAME".iso master } # This is the final "Congratulations!" to the user for letting the script do all the thinking :) success () { clear ; bold ; green echo -e "\nWell, it looks like you made it. Congratulations!\n" echo -e "If all went as planned, you will find \"inx-"$NAME".iso\" " echo -e "in the \""$PWD"\" directory.\n" echo -e "Here is a listing of that directory:\n" white ls -Al } iso_write () { bold ; yellow if ! test $DISPLAY ; then echo -e "\nHit any key to exit Buildinx.\n" read -s -n 1 reset_and_exit fi echo -e "\nWould you like to burn a CD of inx-"$NAME".iso ? [y/n]\n" white read -s -n 1 BURN if [ $BURN = y ] ; then if which nautilus-cd-burner > /dev/null 2>&1 ; then echo -e "\nExiting Buildinx and starting nautilus-cd-burner for you...\n" sleep 3 exec nautilus-cd-burner --source-iso=inx-"$NAME".iso & elif which k3b > /dev/null 2>&1 ; then echo -e "\nExiting Buildinx and starting k3b for you...\n" sleep 3 exec k3b inx-"$NAME".iso & else echo -e "\nI looked for nautilus-cd-burner or k3b... Looks like you prefer something else :)" echo -e "Exiting the Buildinx script - have fun!\n" sleep 4 fi else echo -e "\nOK, exiting the Buildinx script - have fun!\n" sleep 3 fi } reset_and_exit () { tput sgr0 # reset terminal to original state clear exit 0 } main () { warning debootstrapping script_install choices add_inx_examples configure_inx make_inx success iso_write reset_and_exit } # Here we go... main