How I updated 4.11 to 6.2 in a single reboot
Yar Tikhiy
yar at comp.chem.msu.su
Tue Feb 20 14:27:20 UTC 2007
Hi all,
I hope the following story will divert a few potential subscribers
from freebsd-eol and save some effort aimed to keep the 4.11 zombie
dancing.
DISCLAIMER: I take no responsibility for inconvenience or damage
that can happen if one uses my story as a how-to guide, especially
without full knowledge of what and why he is actually doing. YMMV.
I had an old machine with its CD-ROM and floppy drives long-dead
and PXE missing from its boot options. It was running 4.11-STABLE.
Facing the virtual EOL of the entire 4-STABLE branch, I wanted to
update the machine's OS to 6.2-RELEASE and forget about it for a
while, bar running freebsd-update occasionally. You'll say I should
have connected a working CD-ROM drive to the machine and used the
opportunity to dust its inside. Alas, I felt too lazy even to walk
to the console. So I chose the path of least effort: to replace
the binaries in the live system through SSH. This might sound like
installing a heart implant without narcosis, but the surgeon was
sufficiently heartless himself.
First, I downloaded the following 6.2 distribution parts from an
FTP server to the machine: base, dict, games, info, kernels, manpages.
Had I wanted a minimal set, it would have consisted of base, kernels,
manpages. (I had been taught well in the past not to save on
manpages irrespective of the system's role.)
Second, I added the system users and groups that had been born
between 4.11 and 6.2. The new users were proxy, _pflogd, _dhcp;
the new groups were proxy, authpf, _pflogd, _dhcp, audit. I just
grabbed the appropriate revisions of src/etc/master.passwd and
src/etc/group from CVSWeb, extracted the new records from them, and
added those records to /etc/master.passwd (using vipw) and /etc/group.
The new master.passwd and group files could also have been extracted
from the base 6.2 distro. Note that the next step, full extraction
of distros, could have resulted in wrong file ownership without
this step.
After that, I set DESTDIR to /usr/tmp:
# mkdir /usr/tmp
# export DESTDIR=/usr/tmp
and ran install.sh in each distro subdirectory:
# for d in base dict games info manpages
> do
> ( cd $d && sh install.sh )
> done
# cd kernels && sh install.sh GENERIC
So I got the new system's tree under /usr/tmp.
At that stage, the old 4.11 tar(1) slightly mishandled the new
archive format of the 6.2 distros by creating bogus PaxHeader
directories instead of setting the schg flag on some files from the
base distro. Fortunately, each PaxHeader directory hinted at which
files should have had the schg flag set. Due to that I was able
to save the list of files needing the flag.
# find /usr/tmp -path "*/PaxHeader/*" | sed -e s,/PaxHeader,, -e s,/usr/tmp,, > /root/schg.lst
I didn't set the flag on the files immediately because the files
to be transferred to / would lose it anyway. (See below.) Then
it was safe to remove the bogus directories:
# find /usr/tmp -name PaxHeader | xargs rm -rf
Having done with that, I created a scratch directory in / and
transferred the respective files to it to make sure they fit in /:
# mkdir /newroot
# cd /usr/tmp
# tar cf - COPYRIGHT bin boot dev etc lib libexec rescue sbin | ( cd /newroot && tar xpf - )
Note my having used tar(1), not cp(1) there. Alas, cp(1) would
mishandle hard links by copying each link as a separate file. OTOH,
tar(1) would lose file flags -- that's why I had saved the schg
candidate list.
Then I fixed the default kernel:
# cd /newroot/boot
# cp -pR GENERIC/* kernel/
... updated the boot blocks:
# disklabel -B -b /newroot/boot/boot1 -s /newroot/boot/boot2 ad0s1
... created the new system directories in /var (the others could wait):
# cp -pR /usr/tmp/var/named /var/
# mtree -deU -f /newroot/etc/mtree/BSD.var.dist -p /var
The mtree command was the second to require that the new system
users and groups be created in advance.
Then I transferred the most essential settings from /etc to
/newroot/etc for the machine to be up and accessible over the network
after a reboot. The following files could simply be copied: fstab,
resolv.conf, ssh/*key*.
A few files in /newroot/etc called for manual work:
- rc.conf (see below);
- master.passwd (added myself and the rest of local users);
- group (the most important thing was to assign myself to the wheel group);
- mail/aliases (set the root alias.)
I also made sure I didn't have a shell from /usr/local. In addition,
I disabled all extra services in the new rc.conf. The hostname,
network interface configuration, defaultrouter, and sshd_enable="YES"
were basically needed in my case; stock services using the default
configuration could also be enabled from the outset.
After the preliminary merging of /etc, I built spwd.db and pwd.db
in /newroot/etc:
# pwd_mkdb -p -d /newroot/etc /newroot/etc/master.passwd
To get the correct time zone after the reboot, I also copied
/newroot/etc/localtime from /usr/tmp/usr/share/zoneinfo/Europe/Moscow.
I should have created /newroot/etc/wall_cmos_clock if the machine
hadn't had its hardware clock set in UTC.
I also should have paid attention to /newroot/boot/loader.conf, had
the system needed any modules pre-loaded or tunables pre-set.
Then came the tricky part: replacing the binaries.
I started it by killing off most daemons, especially those running
external programs:
# killall cron inetd sendmail syslogd
At the same time, I killed neither sshd nor dhclient, or else I
would lose my connection to the host. To prevent other users from
logging into the system under update, I opened a couple of spare
SSH sessions for myself just in case, then touched /var/run/nologin.
There were no other users logged in at that moment.
Actual replacing began from /usr:
# cd /usr/tmp/usr
# for d in bin games include lib libdata libexec sbin share src
> do
> mv /usr/$d /usr/$d.old && mv $d /usr/
> done
And now -- /!
# cd /newroot
# mv COPYRIGHT lib libexec rescue /
# for d in boot dev etc sbin
> do
> mv /$d /$d.old && mv $d /
> done
# mv /bin /bin.old
# /bin.old/mv bin /
At last, I crossed my fingers and typed:
# /sbin.old/reboot
It worked! In a couple of minutes I could log into the 6.2 system.
It was time to finish the update.
First, I applied the rest of /etc/mtree files except BSD.x11*:
# cd /etc/mtree
# mtree -deU -f BSD.root.dist -p /
# mtree -deU -f BSD.usr.dist -p /usr
# mtree -deU -f BSD.sendmail.dist -p /
# mtree -deU -f BSD.include.dist -p /usr/include
# mtree -deU -f BSD.local.dist -p /usr/local
Then the schg candidate list in /root/schg.lst came handy:
# xargs chflags schg < /root/schg.lst
After that, I copied /etc/exports from /etc.old and manually merged
local bits in rc.conf, inetd.conf, syslog.conf, newsyslog.conf, and
shells. It would have had more sense to unpack the system sources
and run mergemaster(8) against the /etc.old contents if I had had
more local changes there. The commands I would have issued are as
follows:
# mv /etc /etc.new
# cp -pR /etc.old /etc
# mtree -deU -f /etc.new/BSD.root.dist -p /
# mergemaster -i
# cp /etc.new/localtime /etc
Mergemaster would also have taken care of the dot-files in / and
/root. I just overwrote them with the new versions as I hadn't had
customized them.
Further polishing included pkg_add'ing compat[45]x, enabling in
rc.conf and starting important services from the base system and
/usr/local (it's easy due to rc.d!) Now I'm going to update old
ports gradually.
Some day I'll remove the *.old stuff and say the last farewell to
the good old 4.11. It did an excellent job for me, and I'll miss
it.
That's all, folks!
P.S. To refresh my memory, I reproduced my scenario later in a
virtual machine using 4.11-RELEASE. I was also interested in
estimates for disc space usage during the process, as I was going
to update even older and weaker machines in this manner. Here are
some df(1) readings for the curious:
Original 4.11 w/o catman, docs or src:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 36114 149284 19% /
/dev/ad0s1f 1610806 109076 1372866 7% /usr
/dev/ad0s1e 50350 318 46004 1% /var
procfs 4 4 0 100% /proc
After unpacking 6.2 to /usr/tmp from an external partition:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 36116 149282 19% /
/dev/ad0s1f 1610806 270066 1211876 18% /usr
/dev/ad0s1e 50350 318 46004 1% /var
procfs 4 4 0 100% /proc
After copying the respective files to /newroot:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 72884 112514 39% /
/dev/ad0s1f 1610806 270066 1211876 18% /usr
/dev/ad0s1e 50350 318 46004 1% /var
procfs 4 4 0 100% /proc
After copying GENERIC/* to kernel/:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 95896 89502 52% /
/dev/ad0s1f 1610806 270066 1211876 18% /usr
/dev/ad0s1e 50350 318 46004 1% /var
procfs 4 4 0 100% /proc
So, if you have a tiny / partition, you can mv instead of cp the
kernel files from GENERIC/ to kernel/ and save some / space. See
below.
After the reboot:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 95926 89472 52% /
devfs 1 1 0 100% /dev
/dev/ad0s1f 1610806 270068 1211874 18% /usr
/dev/ad0s1e 50350 316 46006 1% /var
procfs 4 4 0 100% /proc
After removing all old and tmp cruft:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 66916 118482 36% /
devfs 1 1 0 100% /dev
/dev/ad0s1f 1610806 124096 1357846 8% /usr
/dev/ad0s1e 50350 318 46004 1% /var
procfs 4 4 0 100% /proc
After removing /boot/GENERIC, which was a duplicate of /boot/kernel:
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 201518 43924 141474 24% /
devfs 1 1 0 100% /dev
/dev/ad0s1f 1610806 124096 1357846 8% /usr
/dev/ad0s1e 50350 320 46002 1% /var
procfs 4 4 0 100% /proc
It is clear from the first and last readings that disc space usage
by 6.2 hasn't grown much as against 4.11, although there are a lot
of important new features in 6.2. FreeBSD isn't like Some Other
OS Types(tm), which double their disc space requirements on each
major release. Great job, folks!
--
Yar
More information about the freebsd-stable
mailing list