[TriLUG] anaconda?

rpjday rpjday at mindspring.com
Fri Dec 14 16:59:47 EST 2001


On Fri, 14 Dec 2001, Christian J Hedemark wrote:

> Hey y'all
> 
> I'm trying to find some doc on anaconda and coming up pretty blank.  The
> help file that shipped with RH 7.2 has a few sparse command line options.
> freshmeat & sourceforge don't seem to have any project page indexed for it.
> Even google seems to have a hard time producing useful hits.  Is anaconda
> strictly internal to Red Hat?  Or is there a public project page somewhere
> with more info?

given the interest there was in discussing anaconda, red hat recently
created an anaconda-devel mailing list.  that's your best bet.

and i've attached my (admittedly incomplete) mini-doc on anaconda.
have fun.

rday

-- 
Robert P. J. Day
Eno River Technologies, Chapel Hill NC
Unix, Linux and Open Source corporate training
-------------- next part --------------
* Overview

  This mini-doc explains, in perhaps excruciating detail, the 
steps of the installation process for Red Hat 7.2, concentrating 
particularly on the anaconda loader program.  Once you understand 
what happens in a typical installation, it's that much easier to 
deal with more complicated tasks like configuring kickstart and 
building your own installation CDs.

  While Red Hat supports a number of installation "methods", such 
as local CD-ROM or hard drive, and network-based installs like
NFS, FTP and HTTP, I'm going to restrict this document to local
CD-ROM installs without any fancy side trips.  Once you see how
this works, it should be easy to generalize.

  In terms of pre-requisites, you should be able to mount floppies,
CD-ROMs and filesystem images (using the mount "-o loop" option), 
and copy directory structures from one place to another.  If you're 
really ambitious, it wouldn't hurt to be able to read bash shell and 
python scripts as well.

  If you have any comments, suggestions or corrections for this
document, feel free to email me at rpjday at mindspring.com.
* Getting prepared -- the necessary parts

  The entire installation process involves poking around in:

  1) boot.img, the CD-ROM-based boot floppy image
  2) both Red Hat install CDs 1 and 2, particularly the 
     installation files and directories in RedHat/base/ on CD 1
  3) the "anaconda" loader RPM (particularly, the src rpm so you 
     can read the source C files and scripts as I explain them)

  I'm assuming you know how to mount the boot.img boot floppy 
in the RedHat/images directory of CD 1 and make your own copy
of the contents.  While you're at it, make your own copies of
at least install CD 1, just so you don't have to keep mounting
it.

  Finally, get the anaconda-7.2-7.src.rpm off of one of the 
source CDs, and:

  # rpm -ivh anaconda-7.2-7.src.rpm

This installs the components of this RPM under the directory
/usr/src/redhat/{SPECS,SOURCES}.  Then:

  # cd /usr/src/redhat/SPECS
  # rpm -bp anaconda.spec

This last step does a "prep" of the source RPM, so you can see
all of the source files with:

  # cd ../BUILD/anaconda-7.2
  # ls

Note that "rpm -bp" doesn't do an actual build, but just unloads
the tarball and applies the patches, which is fine since all you
want to do is look at the source -- you have no need to actually
build the executables.  Now you have all the parts.
* The big picture

  Just so you have a vague idea of where this is all going, the
entire CD-ROM-based installation consists of:

  1) the boot.img boot floppy, which contains
  2) the "vmlinuz" kernel, which unpacks and mounts
  3) the "initrd.img" initial root filesystem ram disk, after which is run
  4) the "init" early stage of the anaconda loader, which invokes
  5) the next "loader" stage of anaconda, which mounts both
  6a) /mnt/source -> Red Hat CD 1
  6b) /mnt/runtime -> RedHat/base/stage2.img, finally running
  7) /usr/bin/anaconda, the python-based graphical installer

Got all that?  So let's start with the boot floppy.
* The SYSLINUX boot floppy

  The boot floppy boot.img (and bootnet.img as well, for that matter)
is a SYSLINUX-style floppy, where SYSLINUX is a boot loader that
operates off of an MS-DOS FAT filesystem, and I'm assuming you've
made a copy of it so you can poke around.

  The top-level contents:

  ldlinux.sys	       part of the SYSLINUX boot process
  syslinux.cfg	       the SYSLINUX config file
  vmlinuz	       the compressed Linux kernel
  initrd.img	       the initial ram disk
  *.msg		       message help files, if you press F<n>

  The syslinux.cfg file controls the boot process, and pretty closely
resembles a LILO configuration file.  Here's the contents of that file
as it appears on the boot.img boot floppy (most of it being self-
explanatory):

  default linux			(default label to boot ... like, duh)
  prompt 1			(yes, you want a boot prompt)
  timeout 600E			(timeout in 1/10 of a second)
  display boot.msg		(initial boot screen to print)
  F1 boot.msg
  F2 general.msg
  F3 expert.msg
  F4 param.msg
  F5 rescue.msg
  label linux			<------ default stanza
    kernel vmlinuz
    append initrd=initrd.img lang= devfs=nomount ramdisk_size=7168 vga=788
  label text
    kernel vmlinuz
    append initrd=initrd.img lang= text devfs=nomount ramdisk_size=7168
  label expert
    kernel vmlinuz
    append expert initrd=initrd.img lang= devfs=nomount ramdisk_size=7168
  label ks
    kernel vmlinuz
    append ks initrd=initrd.img lang= devfs=nomount ramdisk_size=7168
  label nofb
    kernel vmlinuz
    append initrd=initrd.img lang= devfs=nomount nofb ramdisk_size=7168
  label lowres
    kernel vmlinuz
    append initrd=initrd.img lang= lowres devfs=nomount ramdisk_size=7168

  As you can probably tell from the above, if you just let the boot:
prompt timeout, or hit ENTER, you'll be loading the vmlinuz kernel,
which will load and mount the initrd.img ramdisk to start the
installation process.  So what's in the initrd.img ramdisk?

(NOTE:  For more information on SYSLINUX, check the documentation file
/usr/share/doc/syslinux-???/syslinux.doc.)
* The "initrd" ram disk

  The "initrd.img" file represents an initial root file system that
will be mounted by the kernel as the next step in the installation
process.  This file is (like a number of .img files) a gzipped
ext2 filesystem, so you can extract its contents into a separate
directory with commands like the following (you may need root
privilege for some of them):

  $ gunzip -c initrd.img > initrd.tmp	(extract gunzip'ed filesystem)
  $ mkdir m				(create a temporary mount point)
  $ mount -o loop initrd.tmp m		(mount the filesystem)
  $ cp -av m initrd.d			(copy to, say, "initrd.d")
  $ umount m				(finished, so umount it)
  $ rm initrd.tmp			(no need for initrd.tmp anymore)
  $ rmdir m				(no need for mount point either)

  The initrd.d directory now contains what is a minimal root-lookalike
filesystem, with contents:

  bin/ dev/ etc/ linuxrc lost+found/ modules/ proc/ sbin/ tmp/ var/

(Side note 1:  This isn't the *entire* contents of the initrd.img ramdisk
file.  All of the special device files in the /dev directory could not
be duplicated using a simple copy so, if you want to see the actual
contents of /dev, you'll have to do it while you have the filesystem
image mounted.)

(Side note 2:  You might want to poke around the ram disk image, just
to see what the kernel has mounted early in the install process.  In
particular, it might be worth seeing the modules that are available.
If you cd into the "modules" directory, there is a "modules.cgz"
file, a gzipped cpio archive, that you can examine with:

  $ gunzip -c modules.cgz | cpio -itv

Note that the modules in this boot floppy image are appropriate for
a CD-ROM install.  If you were to do this exercise with the
bootnet.img floppy, you'd notice modules for network card support
instead, just as you'd expect.)

  So what happens now?  When using an initrd ramdisk, the boot process
is:

  1) the boot loader loads the kernel and the initial RAM disk
  2) the kernel converts initrd into a "normal" RAM disk and
     frees the memory used by initrd
  3) initrd is mounted read-write as root
  4) the /linuxrc program is executed (this can be any valid 
     executable, including shell scripts; it is run with uid 0 
     and can do basically everything init can do)

  Since /linuxrc -> /sbin/init, we now move on to discussing what
that "init" program does as the next stage in the install process.

(One last side note:  Given that you can see how to pull a boot floppy
image apart, you should also see that it's fairly easy to put it back
together, including replacing some of the parts with updated parts
to build perhaps a new, improved boot floppy -- something to remember 
for later on if you need to consider replacing the init program or 
the loader program with a newer version.)
* The "init" program

  The /sbin/init program that is invoked now is *not* the same
/sbin/init you're familiar with in a typical Red Hat installation.
Rather, it's the first stage of the anaconda installation
program, and you can examine its source back in the BUILD
directory, along with the next stage of anaconda, loader.c:

  /usr/src/redhat/BUILD/anaconda-7.2/loader/
					    init.c
					    loader.c

  If you're watching carefully during the early stages of a
CD-ROM-based install, once the kernel messages are printed, you'll
see output from "init" (if you're feeling ambitious, match this up
with the actual source code from init.c, whose main() routine
starts around line 500 in that file):

  Greetings.
  Red Hat install init version <whetever> starting
  mounting /proc filesystem... done
  mounting /dev/pts (unix98 pty) filesystem... done
  checking for NFS root filesystem...no
  trying to remount root filesystem read write... done
  checking for writeable /tmp... yes
  running install...
  running /sbin/loader

 In addition to the obvious work that init has done based on the
above output, it also:

  1) opens a file descriptor for the first virtual console,
     and dup2()s all of 0, 1 and 2 to that file descriptor
  2) sethostname("localhost.localdomain")
  3) setdomainname("")

and, as its final task, does a fork() and execve() to invoke
"loader", the next installation stage and also a part of the
anaconda loader.  And it's the loader program that really 
starts to do some serious work.
* The "loader" program

  The loader program (loader.c in the anaconda RPM) now picks up
where init left off, and continues with the install.  Given that
loader.c is over 3000 lines long, I'll just hit the high spots.

  One of the duties of "loader" is to parse and process all of
the possible boot-time options you could have typed in at the
boot prompt for an install -- options like "text", "upgrade",
"install", "expert" or any of a number of kickstart directives,
among others.  If you're interested, you can look in loader.c
around line 1900, the routine parseCmdLineFlags() to see all
the possibilities.  It's worth noticing that loader gets the
entire boot: line, not as command-line arguments from the "init"
program, but from reading the file /proc/cmdline, which always
represents the entire line passed to the kernel at boot time.
You can, if you want, at any time see what that line is on a
running box by typing:

  $ cat /proc/cmdline

  So, the major functions of loader.c, if you care to follow
along starting at its main() routine (I'm going to abbreviate
functions calls for brevity):

  - parseCmdLineFlags(), to parse the boot-time options line
  - checkForRam()
  - mlLoadDeps()
  - mlLoadModule()
  - ideSetup()
  - scsiSetup()
  - usbInitialize()
  - setFloppyDevice()

  Down around line 2785, we have the doMountImage() function call
which mounts both the entire Red Hat CD 1, as well as the file
RedHat/base/stage2.img, which represents a much more complete
root filesystem than the one we've been using out of initrd.img
until now.

  - doMountImage("/mnt/source", ...), which calls
      setupCdrom(...), which does (in effect):

      - mknod /tmp/cdrom
      - mount /tmp/cdrom /mnt/source	(mount CD 1)
      - mount -o loop /mnt/source/RedHat/base/stage2.img /mnt/runtime

  In other words, once this call is done:

1) /mnt/source -> CD 1
2) /mnt/runtime -> RedHat/base/stage2.img off of CD 1, a "ramfs"

  Now that loader has a much more complete root filesystem at
/mnt/runtime, it step by step uses the following code to switch
from the directories and files on the initrd filesystem to the
ones in stage2.img:

	unlink("/usr");
	symlink("mnt/runtime/usr", "/usr");

	unlink("/lib");
	symlink("mnt/runtime/lib", "/lib");

	unlink("/modules/modules.dep");
	unlink("/modules/module-info");
	unlink("/modules/pcitable");
	symlink("../mnt/runtime/modules/modules.dep",
		"/modules/modules.dep");
	symlink("../mnt/runtime/modules/module-info",
		"/modules/module-info");
	symlink("../mnt/runtime/modules/pcitable",
		"/modules/pcitable");

	unlink("/modules/modules.cgz");
	symlink("../mnt/runtime/modules/modules.cgz",
		"/modules/modules.cgz");

  And some of the last several steps of the loader code:

  - spawnShell(/dev/tty2)      # unless you specified "noshell"
  - ideSetup()
  - scsiSetup()
  - busProbe()
  - load modules for RAID, msdos, vfat, ext3 and Reiser filesystems
  - usbInitializeMouse()

  Having done all this, loader.c preps to finally invoke the python
script "anaconda" by constructing the command line.  Assuming that
we're still discussing a CD-ROM-based install, loader writes to the
temporary file /tmp/method the installation method, which might
contain:

  cdrom://hdc/mnt/source

  Next, the loader program builds the first part of the anaconda
invocation command as:

  /usr/bin/anaconda -m @/tmp/method

and, finally, appends to that all of the boot-time options you
chose, so you might end up with the anaconda invocation command:

  /usr/bin/anaconda -m @/tmp/method --expert --lowres (etc etc)

  The tail end of the loader program consists of printing:

  Running anaconda - please wait...

then execv()ing anaconda.  Piece of cake.  And now, on to anaconda,
for which a working knowledge of python would be reeeeeeally handy.

Humongous side note:
  
  Once anaconda starts to run, you just have to sit there until the
first graphical screen comes up; typically, the language selection
screen.  But before you go on to the next section describing the 
anaconda loader, you can see the results of having run "loader" as
follows:

1) Switch back to virtual console 1, where you should see the line
   "Running anaconda - please wait ...", the last thing printed by
   loader.c before it invokes anaconda.  Everything printed after
   that was printed by anaconda.

2) Switch to virtual console 2, which should be running a bash shell,
   and verify what was mounted and where with:

   # mount
   /dev/root / ext2 rw 0 0
   /proc /proc proc rw 0 0
   /dev/pts /dev/pts devpts rw 0 0
   /proc/usb/usb /proc/usb/usb usbdevfvs rw 0 0
   /tmp/cdrom /mnt/source iso9660 ro 0 0
   /tmp/loop0 /mnt/runtime cramfs ro 0 0

3) Finally, while you're still at the shell prompt, type "ps" to
   see what's currently running, which should include a bash shell,
   anaconda and X, among other things.

Short side note:

 Just as you can modify, recompile and replace the "init" program
in the boot.img boot floppy, there are reasons why you might want
to hack loader.c and create a rebuilt boot floppy with that.  For
the gory details, see Appendix A.
* anaconda

  --- in progress, while i learn python --- :-)
* Appendix A -- hacking loader.c

  It's possible (and actually pretty easy) to hack the code for 
loader.c.  You might want to do this to add some weird feature
or option you find useful, or to just fix a known bug.  For just
the latter kind of example, take a look at an entry from the
Red Hat kickstart mailing list, 
  http:/www.redhat.com/mailing-lists/kickstart-list/msg01186.html

  The short version: 

  1) hack the code
  2) recompile to get a new executable
  3) unpack a boot floppy (or boot floppy image), replace "loader", 
     and repack it with the new loader
  
  OK, in a little more detail.  You should start by installing the
anaconda source RPM, and build it with:

  # rpm -bc anaconda.spec

This not only unpacks the tarball and applies the patches, but
compiles all of the code, building executables and archive files
under the BUILD directory.

  Once this completes, cd to the BUILD loader directory, make your
changes to loader.c, then

  # make loader		(create the new executable)
  # strip loader	(strip it to make it smaller)

  Now, for safety, make a copy of the standard boot.img somewhere
in an isolated directory, and do the following, preferably as root
for convenience:

  # mkdir boot				(make a mount point)
  # mount -o loop boot.img boot		(mount boot.img there)
  # zcat boot/initrd.img > initrd	(extract/gunzip initrd.img)
  # mkdir id				(make a mount point for that)
  # mount -o loop initrd id		(mount it)
  # cp new-loader-file id/sbin/loader	(overwrite the existing loader)
  # umount id				(unmount the new initrd)	   
  # gzip -9c initrd > initrd.img	(compress it back again)
  # rm boot/{expert,general,param,rescue}.msg	  (make room for new loader)
  # cp initrd.img boot/initrd.img	(replace initrd.img with new one)
  # umount boot				(unmount entire new boot image)
  # dd if=boot.img of=/dev/fd0 bs=96k	(build new boot floppy)

  Two notes about the above:

1) You really do need to use the "-9" option with gzip for maximum
   compression.

2) You really do need to remove the .msg files from the boot image
   to make room for the new initrd.img image since, for reasons I
   still haven't figured out, the new loader, even stripped, is
   larger than the original loader.  Go figure.

  As a short exercise, hack loader.c to print out some goofy
message just before it invokes anaconda at the very end, and
verify that the message does indeed get printed.


More information about the TriLUG mailing list