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:
[root@monty 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:
<groupreq>dialup</groupreq>
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:
[root@monty 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:
[root@monty distro]# mv /distro/i386/CentOS/RPMS/*.rpm /distro/rpms/ [root@monty distro]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/ [root@monty 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:
: : [root@monty rpms]# mv specspo-9.0.92-1.3.noarch.rpm slocate-2.7-13.el4.6.i386.rpm /distro/i386/CentOS/RPMS/ [root@monty rpms]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/ [root@monty 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!
: : [root@monty rpms]# mv usermode-1.74-1.i386.rpm ../i386/CentOS/RPMS/ [root@monty rpms]# /usr/lib/anaconda-runtime/genhdlist --withnumbers --productpath=CentOS /distro/i386/ [root@monty 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):
anaconda anaconda-runtime anaconda-help anaconda-product busybox busybox-anaconda memtest86+ fonts-xorg-base xorg-x11 joe kernel*
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 /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...
[root@monty 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.
Basically I've been back at it, trying to do a buildinstall, and with every progress I make I continue to get the same error when the CD boots - no cd was found that matches your boot media.......
I've hacked into the source for anaconda and filled it up with some debug outputs while getting to know my way around a little bit, and I've found out where its going wrong. The buildstamps do not match.
I have one buildstamp (/.buildstamp) that is stamped 200605251433.0935 and another one (/mnt/runtime/.buildstamp) that is stamped 200603142328.0935. That causes verifyStamp (found in loader2/method.c) to fail, causeing the message.
Now I know the cause, how do I find the solution? I see that buildinstall.py calls mk-stamp, which puts the current date/time/arch into /tmp/makebootdisk.dir.$$/.buildstamp, which appears to be compiled into the initrd.img on the cdrom (/isolinux/initrd.img). So the question is where is it getting the 200603 build stamp (original CentOS build is my guess). And why doesn't the buildinstall process use the same buildstamps for each place. I could hack mk-images to hard code the 200603 stamp, but I would prefer it to stamp it to the correct build date/time.
Any information would be fantastic.
Nice post. I'd be interested in knowing how your Xen hacking is going. How does RHEL 5 (and CentOS 5) including Xen figure into the picture?
Well, if they do it, there is no point on me spending my time in it :-)
Hi Roberto
This is really a nice post from you.
Lately I have been trying to build a custom Distro so that I can distribute to the students. (www.linux-manipur.org). This post happens to be a great help to me though I haven't tried it so far.
Can you please update the latest version of your post.
Also send me you email id so that I can get in touch with you for my distro.
Regards
~TK
I really appreciate your post and you explain each and every point very well.Thanks for sharing this
information.And I’ll love to read your next post too.
Regards:
NABH
Hi very nice article