2006-04-07 22:25

How to make your own distro in 3 not-so simple steps

This is a work in progress. There may be changes every now and then.

Right now, this is version 0.1 and many things are missing.

I wrote in my blog that I missed not having a tool to easily create my own distro. Ok, there are some things around that will do it, but not the distro I wanted, which was to be a CentOS derivative.

So, I delved deep into google and found just enough info to make it work. This page was very useful (In fact, 80% of what I do is taken from there).

It's not pretty, but it's not deadly, either. What it is is barely documented, so let's try to fix that.

In this article, I will try to create my own CentOS distribution (it will someday try to be a CentOS derivative with emphasis on Xen called Xentos ;-).

We will not get there today, but we will create our own smaller distro.

I will assume you are doing all this as root. Please don't tell me that what I tell you here ate your machine, please?

Do it in a qemu virtual box! Have backups! Do it as a regular user instead!

Step 1: Setting up our playground

Get CD1 of CentOS (or your favourite Anaconda+RPM based dsitro, I suppose).

Then, make a copy of it into a folder of your choice. Here, I will use /distro/i386:

mkdir -p /distro/i386
cp -Rvf /media/cdrom/* /distro/i386

Now, you must make a decision. Is it going to be a distro you will show someone? Is it going to be substantially different from CentOS or whatever your original distro is?

If yes, you will need to patch anaconda itself so it uses the right paths for your files and such, and change a bunch of the options we pass to commands in this document.

In the CentOS anaconda rpm, that's just touching the patch CentOS already did to change that from "RedHat" to "CentOS"... and changing it, in my case, to "XentOS", rebuilding and using the new, patched anaconda and anaconda-runtime RPMs.

If you are just playing, or want to test things for a while, just use your distro's name and be happy, because it's simpler. In that case, just use CentOS as the name.

Besides, it will tell me if you are paying attention ;-)

So, if you choose a name for your new distro, rename the CentOS (or whatever) folder:

mv /distro/i386/CentOS /distro/i386/XentOS

Now we can check the sanity of our setup (requires anaconda-runtime):

/usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/

You should get no errors, and now you should have a new set of hdlist files:

[[email protected] distro]# ls /distro/i386/CentOS/base/hdli* -l
-rw-r--r--  1 root root 2355640 abr  7 16:50 /distro/i386/CentOS/base/hdlist
-rw-r--r--  1 root root 4735400 abr  7 16:50 /distro/i386/CentOS/base/hdlist2

Step 2: Customizing your package selection

This is the fun part, and it is also trickier.

Mostly, you need to edit /distro/i386/CentOS/base/comps.xml. Which is a huge XML file. Here's how.

The file has a list of <group> tags at the beginning and later, at the end, a <grouhierarchy> tag.

A group is a group of packages. That's what you use later, in the installer, to decide what you can install. The comps.xml we have now is that for the whole distro. That's why it's so long.

I will show you how to reduce it to the very basics, so you can later hack it to be whatever you want.

I removed all groups except for core and base (the first two). Be careful not to remove the <grouphierarchy> piece.

Also removed this because I removed the dialup group:


Then, in the grouphierarchy section, I removed the following categories: Desktops, Applications, Servers, Development, System... and you end with an empty grouphierarchy ;-)

Whenever you edit comps.xml, you need to rerun genhdlist as above:

/usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/

Now, you have a slimmer install, but the RPMS folder is full of unnecessary packages. To fix that, you will need a tool that is no longer in recent distros, called getfullcomps.py. I got it back from an old box, you can get it here: getfullcomps.py

Run it like this:

[[email protected] distro]# /usr/share/comps-extras/getfullcomps.py comps.xml /distro i386 > /dev/null
CRITICAL ERROR: Unable to find package mcelog
CRITICAL ERROR: Unable to find package openCryptoki
CRITICAL ERROR: Unable to find package prctl
CRITICAL ERROR: Unable to find package elilo
CRITICAL ERROR: Unable to find package iprutils
CRITICAL ERROR: Unable to find package ppc64-utils
CRITICAL ERROR: Unable to find package s390utils
CRITICAL ERROR: Unable to find package yaboot

Honestly, I don't know what's that. Those packages are not even useful (some don't even come with CentOS), so I simply removed the references to them from comps.xml.

Now this gets boring for a while...

Move all your RPMs elsewhere, Then, rerun genhdlist, rerun getfullcomps.py:

[[email protected] distro]# mv /distro/i386/CentOS/RPMS/*.rpm /distro/rpms/
[[email protected] distro]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/
[[email protected] distro]# /usr/share/comps-extras/getfullcomps.py comps.xml /distro i386 > /dev/null
CRITICAL ERROR: Unable to find package NetworkManager
CRITICAL ERROR: Unable to find package OpenIPMI
CRITICAL ERROR: Unable to find package acl
CRITICAL ERROR: Unable to find package acpid

Obviously, it's complaining because you took away all the packages... but it will only complain about those you really need! So, put those back in (this is a little too much work... try to figure out a script to do it) until running gethdlist and getfullcomps gives no "Unable to find" errors:

[[email protected] rpms]# mv specspo-9.0.92-1.3.noarch.rpm slocate-2.7-13.el4.6.i386.rpm /distro/i386/CentOS/RPMS/
[[email protected] rpms]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/
[[email protected] rpms]# /usr/share/comps-extras/getfullcomps.py comps.xml /distro i386 > /dev/null

CRITICAL ERROR: Unable to resolve dependency chkconfig for NetworkManager
CRITICAL ERROR: Unable to resolve dependency dbus for NetworkManager
CRITICAL ERROR: Unable to resolve dependency dbus-glib for NetworkManager

As you can see, getfullcomps checks dependencies, so that you are not missing any packages you require!

So, we fix those by putting back some more packages!

[[email protected] rpms]# mv usermode-1.74-1.i386.rpm ../i386/CentOS/RPMS/
[[email protected] rpms]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/
[[email protected] rpms]# /usr/share/comps-extras/getfullcomps.py comps.xml /distro i386 > /dev/null

Removing packages from a distro is a pain in the butt. Adding them is easier: you add the ones you want, then you get complaints about the missing ones. I want a tool that gives me a list of all the packages noone requires, instead!

But hey, doing this we are removing over 200 packages.

Also, there are a few packages that are required for the installer to work... but it doesn't tell you!

Add these to your RPMS directory if you don't have them (although some are only for the graphical installer):


Rerun genhdlist and getfullcomps.py (you may need to add some dependencies again).

In real life, you would also remove or add some groups or packages in your comps.xml. Well, I leave that as an exercise for now...

Step 3: Creating the CD

We will create a single CD because this distro is small. If you add more packages you may end with more than one, of course.

Create the /distro/pkgorder file:

export PYTHONPATH=/usr/lib/anaconda
/usr/lib/anaconda-runtime/pkgorder /distro/i386 i386 CentOS| tee /distro/pkgorder.txt

Build the installer images:

/usr/lib/anaconda-runtime/buildinstall --comp xentos --pkgorder /distro/pkgorder.txt \
--product "CentOS Linux" --release "CentOS Linux" --version 0 --prodpath CentOS /distro/i386/

If you get an error like this:

rpm2cpio: /distro/i386/CentOS/RPMS/anaconda-runtime-[0-9]*: No such file or directory

you need to add the anaconda-runtime package in the RPMS folder. I am not sure why this is necessary, but when I didn't, I got errors later. You can find the right package in your distro's CDs, somewhere, then do genhdlist and getfullcomps.py as before. Fix any dependencies issues that arise now, again, just like before.

Also, in a later stage, you will want to use your custom anaconda-images RPM, for example, so your distro has its own banner ;-)

So, you run buildinstall, and you should get stuff like this (it will take a while):

Running buildinstall...
/distro/i386/buildinstall.tree.4526 /distro
Going to run buildinstall again
Building images...
Assembling package list...
Expanding text packages...
Expanding graphical packages...
Running mkcramfs  /tmp/treedir.4542/instimage /tmp/instimage.img.12029
Wrote /distro/i386/CentOS/base/stage2.img (42940k)
Writing .discinfo file
timestamp not specified; using the current time

It should not complain about being unable to link directories, or anything of the kind!

Now, you run splittree. This is only necessary if you have more than one CD, but hey, let's pretend, so you know it for later!

mkdir /distro/source
/usr/lib/anaconda-runtime/splittree.py --arch=i386 --total-discs=1 --bin-discs=1 \
--src-discs=1 --release-string="CentOS Linux" --pkgorderfile=/distro/pkgorder.txt \
--distdir=/distro/i386 --srcdir=/distro/source --productpath=CentOS
First package on disc1: hwdata-0.146.18.EL-1.noarch.rpm
Last package on disc1 : net-snmp-5.1.2-11.EL4.6.i386.rpm
i386-disc1 size: 336M
i386-disc1 size: 336M

And now we should have a /distro/i386-disc1, which is the basis for our ISO image.

First we need to fix the hdlist files again, but this time in i386-disc1:

rm -f /distro/i386-disc1/fedora/base/hdlist*
/usr/lib/anaconda-runtime/genhdlist --withnumbers --fileorder /distro/pkgorder.txt \
/distro/i386-disc[123] --productpath CentOS

So... let's do it:

mkisofs -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \
-boot-load-size 4 -boot-info-table -v -r -T -J -V CentOS-0.0.1-D1 -o \
/distro/CentOS-0.0.1-D1.iso /distro/i386-disc1

If you had more than one disc, you would create the ISO images like this example for disc 2 (but I have not tested it):

mkisofs -JR -l -V CentOS-0.0.1-D2  -o /distro/CentOS-0.0.1-D1.iso /distro/i386-disc2

As you can see, our iso is smaller...

[[email protected] distro]# ls -lh *iso
-rw-rw-r--  1 ralsina ralsina 634M mar 15 06:49 CentOS-4.3-i386-bin1of4.iso
-rw-r--r--  1 root    root    423M abr  7 18:14 CentOS-0.0.1-D1.iso

You can test it on a virtual machine now, for example using VMWare or QEmu.

Where to go from here?

Some issues remain:

  • What is comps.xml's twin yumgroups.xml for?
  • i386/repodata/ has the wrong data in it
  • the graphical installer doesn't work much
  • You can only install "Custom"
  • The Package group selection offers only "Everything"
  • The Installer says CentOS everywhere...
  • Many, many others.

Maybe someday they will be fixed!

Now you have everything in place to update RPMs, or replace them with your own thing... want to boot using runit instead of SysV init? Want to make it leaner? Want to make a desktop 1-CD distro?

Well, get on the job, man! :-)

In the future, I should try hacking anaconda itself, and see what I can find.


Comments powered by Disqus

Contents © 2000-2019 Roberto Alsina