[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