Toucan Linux Project - 9
A Roll Your Own Distribution
Goal 1 – Base System
Stage 2 – Building the Base System
First we will install a few more packages that are part of the TTLP base system that are not included in the LFS base system. Then we will do configuration work on the target system using LFS as the guide. Next time we will do some beginning work for the package manager to be able to rebuild the base system quickly, and then start on the kernel configuration.
Additional Packages
There are several packages we will install besides LFS. This include packages to help identify the hardware on the system, maintain the filesystems, perform text editing with something besides
vim, and bring up the network. These were not included in the list of downloaded packages from LFS. We will need to use the host to fetch them since there are no networking tools in the current base.
Step 1 - Which which?
A note regarding the lack of the program
which. This is a utility to search the
PATH and print the full path of an executable. It is often used in another command such as
file `where tar`
But it is also used simply to find or verify the path of an executable. It has not yet been installed as that is a task which, as it turns out, is a utility many old Unix hacks like to use a lot. For now there are two alternatives. If you just want to know where a binary is located,
util-linux provides the
whereis command.
$ whereis tar
tar: /bin/tar /usr/include/tar.h /usr/share/man/man1/tar.1 /usr/share/info/tar.info /usr/share/info/tar.info-1 /usr/share/info/tar.info-2
This shows the executable file first, the include files, info files, and man pages. This can be used to satisfy the use case of needing to know where an executable is located. The bash shell can also tell the location of an executable in the search path using the following:
$ type -pa tar
/bin/tar
which can be set as an alias in your shell using
alias which="type -ap"
which will handle just about everything you need. If you want a system wide application do the following script
cat > /usr/bin/which << "EOF"
#!/bin/bash
type -pa "$@" | head -n 1 ; exit ${PIPESTATUS[0]}
EOF
chmod -v 755 /usr/bin/which
chown -v root:root /usr/bin/which
which will also most likely handle requirements that most people need it. There is a full fledged (and mostly unnecessary) binary package appropriately called which that we can install later but TTLP will not use the binary as there simply is no longer any need. I suggest creating the script above. Some of the packages will use which in the configure to find the utilities they need.
Step 2 - Acquiring The Tarballs
First create the file to download them:
cat > wget_addl.txt << EOF
https://www.kernel.org/pub/linux/utils/fs/xfs/xfsprogs/xfsprogs-5.2.0.tar.xz
https://downloads.sourceforge.net/joe-editor/joe-4.6.tar.gz
https://www.kernel.org/pub/software/utils/pciutils/pciutils-3.6.2.tar.xz
https://www.kernel.org/pub/software/network/iw/iw-5.3.tar.xz
https://github.com/thom311/libnl/releases/download/libnl3_4_0/libnl-3.4.0.tar.gz
https://ftp.gnu.org/gnu/wget/wget-1.20.3.tar.gz
https://roy.marples.name/downloads/dhcpcd/dhcpcd-8.0.2.tar.xz
https://w1.fi/releases/wpa_supplicant-2.9.tar.gz
https://www.kernel.org/pub/linux/utils/usb/usbutils/usbutils-012.tar.xz
https://github.com//libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2
https://cpan.metacpan.org/authors/id/C/CH/CHORNY/Tie-IxHash-1.23.tar.gz
https://downloads.sourceforge.net/infozip/zip30.tar.gz
https://downloads.sourceforge.net/infozip/unzip60.tar.gz
EOF
Then using a terminal or virtual console in the host, download them with
wget -input-file=$LFS/sources/wget-addl.txt --continue --directory-prefix=$LFS/sources
Now we will use the
cci.pl script to install them. As before make sure you have the
confs variable set and you are in the directory where the
cci.pl program is located
~/cci.
Step 3 – xfsprogs 5.2.0
This package contains the utilities that allow maintaining the XFS filesystems, such as defragmentation, repair, and using the special features.
Make the config
echo "./configure --enable-readline" > $confs/config.xfsprogs
For the make we will set the
NDEBUG flag to turn off debugging. It is also important to have
e2fsprogs installed first so we have access to the UUID library (
/lib/libuuid.so.1). That is the primary reason for installing it earlier even though we don't use ext filesystems for TTLP. Purists might decide instead to install
uuid-dev instead but the overhead of the ext programs is small, and if you choose to make a universal bootable USB drive you will probably use ext4 simply for compatibility.
Create the compile to disable debugging
echo "DEBUG=-NDEBUG make" > $confs/compile.xfsprogs
Then the install which will delete the static libraries (even though there is a configure option to turn them off they are used in build process which will cause it to fail if you do), make some links
cat > $confs/install.xfsprogs << EOF
rm -rfv /usr/lib/libhandle.a
rm -rfv /lib/libhandle.{a,la,so}
ln -sfv ../../lib/libhandle.so.1 /usr/lib/libhandle.so
EOF
Build with null for test since there is no test suite
./cci.pl -n xfsprogs -p xfsprogs-5.2.0.tar.xz -c config.xfsprogs -m compile.xfsprogs -t null -i install.xfsprogs
Step 4 – pciutils 3.6.2
This utility will allow us to determine what devices are on the PCI bus to help determine what drivers to include in the kernel.
Make the compile
cat > $confs/compile.pciutils << EOF
make PREFIX=/usr \
SHAREDIR=/usr/share/hwdata \
SHARED=yes
EOF
Create the install. This will also add a weekly job to cron to update the PCI IDs database. We don't yet have cron, but that's okay for now. We'll be revisiting it later with the package manager. If you don't want this functionality remove all lines starting with"#Add to weekly cron" and ending with the "EOF" sentinel.
cat > $confs/install.pciutils << "EOS"
make PREFIX=/usr \
SHAREDIR=/usr/share/hwdata \
SHARED=yes \
install install-lib
chmod -v 755 /usr/lib/libpci.so
# Add to weekly cron
cat > /etc/cron.weekly/update-pciids.sh << "EOF"
#!/bin/bash
/usr/sbin/update-pciids
chmod 754 /etc/cron.weekly/update-pciids.sh
EOF
EOS
Finally build with
./cci.pl -n pciutils -p pciutils-3.6.2.tar.xz -c null -m compile.pciutils -t null -i install.pciutils
Step 5 – libnl 3.4.0
This library provides the APIs to access the netlink kernel interfaces.
Create the config
cat > $confs/config.libnl << EOF
./configure --prefix=/usr \
--sysconfdir=/etc \
--disable-static
EOF
Then build with
./cci.pl -n libnl -p libnl-3.4.0.tar.gz -c config.libnl
Step 6 – iw 5.3
This contains the newer method of configuring wireless devices through the command line. It replaces the older iwconfig which uses the now deprecated wireless extensions interface.
Create the config to install uncompressed man pages
echo 'sed -i "/INSTALL.*gz/s/.gz//" Makefile' > $confs/config.iw
And the install
echo 'make SBINDIR=/sbin install' > $confs/install.iw
Then build with
./cci.pl -n iw -p iw-5.3.tar.xz -c config.iw -t null -i install.iw
Step 7 – wget 1.20.3
The wget utility retrieves files using HTTP, HTTPS, and FTP protocols. It can also be used to download whole websites. We will need to fetch further tarballs and the package manager will use it for fetching source distributions.
Make the config which will use the kernel cryptography routines and OpenSSL
cat > $confs/config.wget << EOF
./configure --prefix=/usr \
--sysconfdir=/etc \
--with-ssl=openssl
--with-linux-crypto
EOF
Then build with
./cci.pl -n wget -p wget-1.20.3.tar.gz -c config.wget
During the testing phase there will be a lot of fails since wget won't handle certificates yet.
Step 8 - joe 4.6
Joe is a console based text editor that operates in several way, way back ways such as Micro-Pro's Wordstar or Borland's "Turbo" language series. It can emulate EMACS, WordStar, and pico. It also has a menu to help you learn if you don't know any of these.
Create the config
cat > $confs/config.joe << EOF
./configure --prefix=/usr \
--sysconfdir=/etc \
--docdir=/usr/share/doc/joe-4.6
EOF
Create the install which will install some of Joe's utilities that aren't installed by default
echo "make install && install -vm 755 joe/util/{stringify,termidx,uniproc} /usr/bin" > $confs/install.joe
There is no test suite so build with
./cci.pl -n joe -p joe-4.6.tar.gz -c config.joe -t null -i install.joe
For normal mode use
joe as the command name. For WordStar mode use
jstar. For EMACS mode use
jmacs. The last option is to make Joe use Pico's key set with
jpico. When it first starts, at the bottom in the status bar is the key combination to bring up the help menu.
This is the only text editor (other than
vim) that TTLP will use until the later stages. You will need a familiar console text editor as it will be awhile before we have a graphical mode. If you prefer another, you'll need to install it yourself. If you want to continue using it, I suggest using the
cci.pl program to create the proper script components.
Step 9 – dhcpcd 8.0.2
This is a DHCP client to help with networking since almost all user networks are DHCP-based.
Create the config
echo "./configure --libexecdir=/lib/dhcpcd --dbdir=/var/lib/dhcpcd" > $confs/config.dhcpcd
and the test
echo "make test" > $confs/test.dhcpcd
Build with
./cci.pl -n dhcpcd -p dhcpcd-8.0.2.tar.xz -c config.dhcpcd -t test.dhcpcd
Step 10 – wpa-supplicant 2.9
This is a program that handles Wi-Fi protected access using WAP, EAP, and roaming.
It configures in a very different way. We first need to create a file of configuration options
cat > $confs/config.wpa-supplicant << "EOS"
cat > wpa_supplicant/.config << "EOF"
CONFIG_BACKEND=file
CONFIG_CTRL_IFACE=y
CONFIG_DEBUG_FILE=y
CONFIG_DEBUG_SYSLOG=y
CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
CONFIG_DRIVER_NL80211=y
CONFIG_DRIVER_WEXT=y
CONFIG_DRIVER_WIRED=y
CONFIG_EAP_GTC=y
CONFIG_EAP_LEAP=y
CONFIG_EAP_MD5=y
CONFIG_EAP_MSCHAPV2=y
CONFIG_EAP_OTP=y
CONFIG_EAP_PEAP=y
CONFIG_EAP_TLS=y
CONFIG_EAP_TTLS=y
CONFIG_IEEE8021X_EAPOL=y
CONFIG_IPV6=y
CONFIG_LIBNL32=y
CONFIG_PEERKEY=y
CONFIG_PKCS12=y
CONFIG_READLINE=y
CONFIG_SMARTCARD=y
CONFIG_WPS=y
CFLAGS += -I/usr/include/libnl3
EOF
EOS
The compile is
echo "cd wpa_supplicant && make BINDIR=/sbin LIBDIR=/lib" > $confs/compile.wpa-supplicant
And the install
cat > $confs/install.wpa-supplicant << EOF
install -v -m755 wpa_{cli,passphrase,supplicant} /sbin/
install -v -m644 doc/docbook/wpa_supplicant.conf.5 /usr/share/man/man5/
install -v -m644 doc/docbook/wpa_{cli,passphrase,supplicant}.8 /usr/share/man/man8/
EOF
Build with
./cci.pl -n wpa-supplicant -p wpa_supplicant-2.9.tar.gz -c config.wpa-supplicant -m compile.wpa-supplicant -t null -i install.wpa-supplicant
Step 11 – Tie::IxHash
This provides the ability to maintain the entry order of hashes in Perl, something we will need for the package manager.
Make the compile
echo "perl Makefile.PL" > $confs/compile.tie-ixhash
There is no test or config. To build use
./cci.pl -n tie-ixhash -p Tie-IxHash-1.23.tar.gz -c null -m compile.tie-ixhash -t null
Step 12 - libusb 1.0.22
This library provide routines for accessing USB devices. It is needed by the *usbutils* we are installing as part of the base system.
Create the config
cat > $confs/config.libusb << "EOF"
sed -i "s/^PROJECT_LOGO/#&/" doc/doxygen.cfg.in
./configure --prefix=/usr --disable-static
EOF
Parallel building cause problems. Create a compile to disable it
echo "make -j1" > $confs/compile.libusb
There is no test suite. Build with
./cci.pl -n libusb -p libusb-1.0.22.tar.bz2 -c config.libusb -m compile.libusb -t null
Step 13 - usbutils
Like the
pciutils package this will help query what hardware is on the system in order to build the kernel.
Create config
echo "./autogen.sh --prefix=/usr --datadir=/usr/share/hwdata" > $confs/config.usbutils
The install will get an updated version of the USB IDs and install a cron job to update them weekly as we did in
pciutils.
cat > $confs/install.usbutils << "EOS"
make install
install -dm755 /usr/share/hwdata/
wget http://www.linux-usb.org/usb.ids -O /usr/share/hwdata/usb.ids
cat > /etc/cron.weekly/update-usbids.sh << "EOF" &&
#!/bin/bash
/usr/bin/wget http://www.linux-usb.org/usb.ids -O /usr/share/hwdata/usb.ids
EOF
chmod 754 /etc/cron.weekly/update-usbids.sh
EOS
The build with
./cci.pl -n usbutils -p usbutils-012.tar.xz -c config.usbutils -t null -i install.usbutils
Step 14 - zip 30
Utilities to create and use ZIP archives
Create the compile
echo "make -f unix/Makefile generic_gcc" > $confs/compile.zip
Create the install
echo "make prefix=/usr MANDIR=/usr/share/man/man1 -f unix/Makefile install" > $confs/install.zip
Build with the following since it doesn't require a config or test.
./cci.pl -n zip -p zip30.tar.gz -c null -m compile.zip -t null -i install.zip
Step 15 - unzip 60
A set of utilities to unarchive files stored in a ZIP archive. Like its opposite,
zip the has no test or config.
Create the compile
echo "make -f unix/Makefile generic" > $confs/compile.unzip
Create the install
echo "make prefix=/usr MANDIR=/usr/share/man/man1 -f unix/Makefile install" > $confs/install.unzip
The install with
./cci.pl -n unzip -p unzip60.tar.gz -c null -m compile.unzip -t null -i install.unzip
Step 16 - Removing Libtool Archive Files
Many of the packages use libtool to build. This causes libtool to create files called "libtool archives" which have a file extension of ".la." This contains information about how to link executables back before the days of the Executable and Linkable Format (ELF) files. They can interfere with future builds so we want to remove them. The LFS team has a great tool for doing this. You can find it on this page that discusses the problem, and why they just can't be removed.
https://www.linuxfromscratch.org/blfs/view/svn//introduction/la-files.html
That page gives a shell script that will clean the files from everything but a single known package
ImageMagic which uses the old
lt_dlopen() which requires the libtool archive file.
A tool to find programs that require the libtool archives follows
cat > /usr/sbin/find_la_deps << "EOF"
#!/bin/bash
# Michael R Stute for The Toucan Linux Project
# Make sure we are running with root privs
if test "${EUID}" -ne 0; then
echo "Error: $(basename ${0}) must be run as the root."
exit 1
fi
# Get the directories from /opt
OPTDIRS=$(find /opt -mindepth 1 -maxdepth 1 -type d)
# Standard directories
DIRS="/bin /usr/bin /sbin /usr/sbin"
echo Searching in $DIRS $OPTDIRS
# Find all executables and use objdump to get symbols
for file in `find $DIRS $OPTDIRS /bin /usr/bin/ /sbin /usr/sbin -type f` ; do
OUT=objdump --syms $file|grep tl_dlopen;
if [ ! -z $OUT ]; then
echo $file uses tl_dlopen function
fi
done 2> /dev/null
EOF
It will output the name of any executable that uses the
tl_dlopen function which will provide a list of files that need the libtool archive. Match these back to the package.
Step 17 - Cleaning Up Static Libraries
Dynamic libraries link at run time and updating the library is all that is needed to update all programs using it. Static libraries get added by the linker to the executable, making the executable larger. While the kernel will load each function from dynamic libraries only a single time and reference it for all programs, functions from static libraries get loaded with every program since they are part of the program's code. When I started working in C static linking was all we had so that was normal. But now we want to use dynamic whenever possible. We will specify
--disable-static as a configuration parameter for every package that supports, but as we've found with
xfsprogs you can't always do that.
There are some static libraries from
libc that are best left as static. Static libraries end with ".a" to indicate an archive. The functions they contain can be viewed with the
nm command and using
grep to select the text (code) sections.
nm libss.a|grep ' T '
0000000000000070 T initialize_ss_error_table
0000000000000000 T initialize_ss_error_table_r
0000000000000000 T ss_create_invocation
0000000000000200 T ss_delete_invocation
0000000000000280 T ss_add_info_dir
00000000000003a0 T ss_delete_info_dir
0000000000000000 T ss_help
0000000000000120 T ss_execute_command
00000000000001d0 T ss_execute_line
0000000000000610 T ss_abort_subsystem
00000000000001e0 T ss_listen
0000000000000630 T ss_quit
0000000000000650 T ss_rl_completion
0000000000000000 T ss_parse
00000000000000e0 T ss_error
0000000000000000 T ss_name
00000000000001a0 T ss_perror
0000000000000020 T ss_get_prompt
0000000000000000 T ss_set_prompt
0000000000000000 T ss_add_request_table
0000000000000100 T ss_delete_request_table
0000000000000000 T ss_list_requests
0000000000000170 T ss_pager_create
0000000000000060 T ss_page_stdin
0000000000000000 T ss_safe_getenv
0000000000000000 T ss_self_identify
0000000000000030 T ss_subsystem_name
0000000000000050 T ss_subsystem_version
0000000000000070 T ss_unimplemented
0000000000000040 T ss_get_readline
One of the biggest problems is that is for some reason the dynamic library is missing the linker will automatically use the static version and you might not even realize it. Later if you upgrade the library the program will not use the new code unless it is recompiled. This is not a problem if your distro is a binary distribution because everything is already compiled, but if it is a compile on the fly this isn't a good idea. TTLP will delete all static libraries possible.
Some libraries must stay, specifically those installed by the
glibc and
gcc packages. For most others we can, and will delete them. Instead of deleting these libraries we will move them to an area not in the linker's search path. This will be
/usr/graveyard. If you install something and it needs a missing library, look there.
Here is a script that will do most the work
cat > /usr/sbin/clean_static << "EOF"
#!/bin/bash
# Move static libraries (but not exceptions) to the graveyard
# Author: Michael R Stute, The Toucan Linux Project
# Never delete these libraries
EXCPTS="libanl.a libasan.a libatomic.a libBrokenLocale.a \
libc.a libc_nonshared.a libcrypt.a libdl.a libg.a \
libgomp.a libitm.a liblsan.a libm-2.29.a libm.a \
libmcheck.a libmvec.a libmvec_nonshared.a libpthread.a \
libquadmath.a libresolv.a librt.a libssp.a \
libssp_nonshared.a libstdc++.a libstdc++fs.a libsupc++.a \
libtsan.a libubsan.a libutil.a"
#Where we put static libraries we don't know about
GRAVEYARD=/usr/graveyard
#Make sure the graveyard and saved area is created
mkdir -vp $GRAVEYARD/saved
#Move the saved files to the saved area
cd /usr/lib
for file in $EXCPTS; do
mv -v $file $GRAVEYARD/saved
done
#Move all the static libraries in /usr/lib
mv -v *.a $GRAVEYARD
#Move the saved files back to /usr/lib
mv -v $GRAVEYARD/saved/* .
#Delete saved area
rmdir $GRAVEYARD/saved
EOF
This will move all the non-essential static libraries to a directory at
/usr/graveyard where they can be resurrected by moving them back to
/usr/lib. At the top is a variable called
EXCPTS which holds a list of the exceptions. These are moved into a newly created directory in the graveyard, all other files ending in ".a" are moved to the graveyard and the exceptions moved back from the special directory. As long as we maintain the list of exceptions this script will do the job.
The only issue is that the libraries will not be in
/usr/lib for a very short time. If there is anything linking during a compile process (since they are static this can only happen at compile) the libraries might be missing. If you have a busy server full of developers that might be a problem, but for your local workstation you are in control.
Step 18 - The init scripts
As already discussed TTLP won't use
systemd but instead will start with SysVInit. As per the standard the following run levels will be created:
0 — halt
1 — Single user mode
2 — Multiuser, without networking
3 — Full multiuser mode
4 — User definable
5 — Full multiuser mode with display manager
6 — reboot
We might first ask what is the initial program? This is the program that the kernel launches once it has completely brought up user land (as opposed to kernel land). This can be any program, including simply a shell such as
bash, that can be used to initialize the system. If all you need is a command line shell, this program could be
bash in which case the kernel would boot, initialize itself to complete kernel land, and then bring up user land and run bash on the console. Instead of a login you would have the bash prompt in which to start running the commands that create the environment. If we look at the kernel sources in the file
init/main.c in the function
kernel_init() we see the following
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
We see here that it tries to execute a command stored in the variable execute_command but if this isn't defined it will try the following:
/sbin/init,
/etc/init,
/bin/init, and finally
/bin/sh, the last being the shell. The initial program can be changed using the
init boot option. This might come in handy if you wanted to try a different boot process.
The purpose of this program is to create whatever we consider the final system with all the modules loaded, daemon programs running, and entry into the system by logging in. The SysVInit process has the concepts of run levels as listed above. They can anything you choose, and for smaller systems, it might only be halt, reboot, and normal, but we'll stick to the standards.
When
init starts up it looks for a file called
/etc/inittab which is a table. This had been discussed in a previous article so we don't need to spend too much time on it here. It will look for the
initdefault entry and set the run level accordingly then run the
sysinit command which is
/etc/rc.d/init.d/rc a Bash script that will go through the process of bringing everything up according to the run level. LFS provides a very good set of startup scripts that will be located in
/etc/init.d/. We will install them and set them up as part of the base system.
We only need the default install. Install using
./cci.pl -n bootscripts -p lfs-bootscripts-20190524.tar.bz2 -c null -m null -t null
They will reside in
/etc/rc.d/init.d. There isn't much a reason to do this more than once, so in the package manager we will be sure to disable installing this package if we want to rebuild everything.
Step 19 - The Init Table
The init program will expect to find a file
/etc/initab. Create it
cat > /etc/inittab << EOF
# Begin /etc/inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/init.d/rc S
l0:0:wait:/etc/rc.d/init.d/rc 0
l1:S1:wait:/etc/rc.d/init.d/rc 1
l2:2:wait:/etc/rc.d/init.d/rc 2
l3:3:wait:/etc/rc.d/init.d/rc 3
l4:4:wait:/etc/rc.d/init.d/rc 4
l5:5:wait:/etc/rc.d/init.d/rc 5
l6:6:wait:/etc/rc.d/init.d/rc 6
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
su:S016:once:/sbin/sulogin
1:2345:respawn:/sbin/agetty --noclear tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600
3:2345:respawn:/sbin/agetty tty3 9600
4:2345:respawn:/sbin/agetty tty4 9600
5:2345:respawn:/sbin/agetty tty5 9600
6:2345:respawn:/sbin/agetty tty6 9600
# End /etc/inittab
EOF
This is the standard LFS startup but it is not much different from the Linux startup from way back in the late 1990s and other Unixes as far back as the 1970s. It declares run level 3 as the default, runs
/etc/rc.d/init.d/rc with the argument "S" as the system initialization script. It then defines six run levels (l0 through l6) with each one being the same
/etc/rc.d/init.d/rc with the run level as the argument. Below is the program to run if the user presses the age-old CTRL-ALT-DEL key combination made famous by MSDOS. It will reboot the system using the
/sbin/shutdown command. After that comes a line to run the single user login (
/sbin/sulogin) for run levels S, 0, 1, and 6. Finally for run levels 2 through 5 launches
agetty (a terminal listener) on the six virtual consoles mapped to CTRL-ALT-F1 through CTRL-ALT-F6.
The rc script works very simply. In the
/etc/rc.d directory is another directory for each run level
$ ls /etc/rc.d
init.d rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rcS.d
Within each of these directories the list of scripts to run for that run level exist, but they are links to the scripts in the
init.d directory above.
$ ls -l rc1.d/
total 0
lrwxrwxrwx 1 root root 17 Jun 13 15:12 K80network -> ../init.d/network
lrwxrwxrwx 1 root root 18 Jun 13 15:12 K90sysklogd -> ../init.d/sysklogd
lrwxrwxrwx 1 root root 16 Jun 28 09:16 S25random -> ../init.d/random
Each file begins with either a 'K' or an 'S' followed by a number and the file name. Those starting with a 'K' are stopped (killed) in the order of the numbers, lowest to highest. This occurs first. Then those starting with an 'S' are started in the order of the numbers, lowest to highest. All of the scripts in
init.d accept the following arguments:
start,
stop,
status, and
restart to start the service, stop the service, check the status of the service, and to stop then start the service (restart). Some will accept others, including
reload which reloads the configuration for a running daemon without stopping it. There are other scripts that accept other arguments, but by standard they should all honor at least
start,
stop, and
restart. The numbers after the first letter represent a method for sequencing the scripts. You want the network up before you start the web service, for instance, so the web server should have a higher number than the network.
The scripts provided by LFS are very modern, using colorization, and several ways to change the defaults. The file
/etc/sysconfig/rc.file can be used to override nearly everything. If you choose to make changes start here.
About the Scripts
checkfs - This script checks the integrity of the file systems before they are mounted. This is perform by calling
/sbin/fsck which will determine the type of filesystem and run the appropriate program as fsck.
fstype (
fsck.ext4,
fsck.xfs,
fsck.cramfs). The XFS filesystems are checked automatically upon mount so the
fsck.xfs program is a simply script to call
xfs_repair if the "force" option is given. (There will be a complete entry dealing with the XFS filesystem maintenance and options.)
cleanfs - Removes files that should not be preserved between reboots, such as those in
/var/run/ and
/var/lock/; it re-creates
/var/run/utmp and removes the possibly present
/etc/nologin,
/fastboot, and
/forcefsck!= files. If the environment variable
SKIPTMPCLEAN is undefined it will delete all files from the
/tmp directory. In older Unixes files often survived a boot in the
/tmp area and users then started relying on them being there. It is better to clean
/tmp between boots so the users will use their own area (such as creating
~/tmp). If the
/etc/nologin file is present, users are not allowed to log into the system. This is a way to stop logins (except for the root user). If
/fastboot is present it will skip checking the filesystems. If
/forcefsck is present it will force an fsck and repair on file system.
console - Loads the correct keymap table for the desired keyboard layout; it also sets the screen font. Since most system will have a framebuffer console the font can often be small. This can be used to change the font to something easier to read.
functions - Contains common functions, such as error and status checking, that are used by several bootscripts
halt - Halts the system
ifdown - Stops a network device (interface down)
ifup - Initializes a network device (interface up)
localnet - Sets up the system's hostname and local loopback device (lo)
modules - Loads kernel modules listed in /etc/sysconfig/modules, using arguments. This is the easiest method to get the kernel to load modules that it does not load automatically
mountfs - Mounts all file systems in
/etc/fstab, except ones that are marked
noauto or are network based
mountvirtfs - Mounts virtual kernel file systems, such as proc, sys, etc.
network - Sets up network interfaces, such as network cards, and sets up the default gateway (where applicable)
rc - The master run-level control script; it is responsible for running all the other boot scripts one-by-one, in a sequence determined by the name of the symbolic links being processed. This is the program specified for the sysinit in
/etc/inittab.
reboot - Reboots the system
sendsignals - Makes sure every process is terminated before the system reboots or halts
setclock - Resets the kernel clock to local time in case the hardware clock is not set to UTC time
ipv4-static - Provides the functionality needed to assign a static Internet Protocol (IP) address to a network interface
swap - Enables and disables swap files and partitions.
sysctl - Loads system configuration values from
/etc/sysctl.conf, if that file exists, into the running kernel. This can be used to change the values in the sys virtual filesystem.
sysklogd - Starts and stops the system and kernel log daemons if the system logging package is sysklogd (which it is in a base LFS system).
template - A template to create custom boot scripts for other daemons.
udev - Prepares the !/dev! directory and starts udev.
udev_retry - Retries failed udev uevents, and copies generated rules files from
/run/udev to
/etc/udev/rules.d if required. This runs after the
mountfs script
We will add more scripts are we add packages to the system.
We will use replace the standard LFS master script with a slight modification to make it easier to change a few things. Do the following
cat > /etc/init.d/rc << "EOF"
#!/bin/bash
########################################################################
# Begin rc
#
# Description : Main Run Level Control Script
#
# Authors : Gerard Beekmans - gerard@linuxfromscratch.org
# : DJ Lucas - dj@linuxfromscratch.org
# Update : Bruce Dubbs - bdubbs@linuxfromscratch.org
# : Michael R Stute - michaelrstute@gmail.com
#
# Version : LFS 7.0 modified for The Toucan Linux Project
#
########################################################################
. /lib/lsb/init-functions
print_error_msg()
{
log_failure_msg
# $i is set when called
MSG="FAILURE:\n\nYou should not be reading this error message.\n\n"
MSG="${MSG}It means that an unforeseen error took place in\n"
MSG="${MSG}${i},\n"
MSG="${MSG}which exited with a return value of ${error_value}.\n"
MSG="${MSG}If you're able to track this error down to a bug in one of\n"
MSG="${MSG}the files provided by the ${DISTRO_MINI} book,\n"
MSG="${MSG}please be so kind to inform us at ${DISTRO_CONTACT}.\n"
log_failure_msg "${MSG}"
log_info_msg "Press Enter to continue..."
wait_for_user
}
check_script_status()
{
# $i is set when called
if [ ! -f ${i} ]; then
log_warning_msg "${i} is not a valid symlink."
SCRIPT_STAT="1"
fi
if [ ! -x ${i} ]; then
log_warning_msg "${i} is not executable, skipping."
SCRIPT_STAT="1"
fi
}
run()
{
if [ -z $interactive ]; then
${1} ${2}
return $?
fi
while true; do
read -p "Run ${1} ${2} (Yes/no/continue)? " -n 1 runit
echo
case ${runit} in
c | C)
interactive=""
${i} ${2}
ret=${?}
break;
;;
n | N)
return 0
;;
y | Y)
${i} ${2}
ret=${?}
break
;;
esac
done
return $ret
}
# Read any local settings/overrides
[ -r /etc/sysconfig/rc.site ] && source /etc/sysconfig/rc.site
DISTRO=${DISTRO:-"Linux From Scratch"}
DISTRO_CONTACT=${DISTRO_CONTACT:-"lfs-dev@linuxfromscratch.org (Registration required)"}
DISTRO_MINI=${DISTRO_MINI:-"LFS"}
# Override the Distro with /etc/sysconfig/distro
[ -r /etc/sysconfig/distro ] && source /etc/sysconfig/distro
# Set interactive boot to no and allow override
IPROMPT=${IPROMPT:-"no"}
[ -r /etc/sysconfig/interactive ] && source /etc/sysconfig/interactive
# These 3 signals will not cause our script to exit
trap "" INT QUIT TSTP
[ "${1}" != "" ] && runlevel=${1}
if [ "${runlevel}" == "" ]; then
echo "Usage: ${0} <runlevel>" >&2
exit 1
fi
previous=${PREVLEVEL}
[ "${previous}" == "" ] && previous=N
if [ ! -d /etc/rc.d/rc${runlevel}.d ]; then
log_info_msg "/etc/rc.d/rc${runlevel}.d does not exist.\n"
exit 1
fi
if [ "$runlevel" == "6" -o "$runlevel" == "0" ]; then IPROMPT="no"; fi
# Note: In ${LOGLEVEL:-7}, it is ':' 'dash' '7', not minus 7
if [ "$runlevel" == "S" ]; then
[ -r /etc/sysconfig/console ] && source /etc/sysconfig/console
dmesg -n "${LOGLEVEL:-7}"
fi
if [ "${IPROMPT}" == "yes" -a "${runlevel}" == "S" ]; then
# The total length of the distro welcome string, without escape codes
wlen=${wlen:-$(echo "Welcome to ${DISTRO}" | wc -c )}
welcome_message=${welcome_message:-"Welcome to ${INFO}${DISTRO}${NORMAL}"}
# The total length of the interactive string, without escape codes
ilen=${ilen:-$(echo "Press 'I' to enter interactive startup" | wc -c )}
i_message=${i_message:-"Press '${FAILURE}I${NORMAL}' to enter interactive startup"}
# dcol and icol are spaces before the message to center the message
# on screen. itime is the amount of wait time for the user to press a key
wcol=$(( ( ${COLUMNS} - ${wlen} ) / 2 ))
icol=$(( ( ${COLUMNS} - ${ilen} ) / 2 ))
itime=${itime:-"3"}
echo -e "\n\n"
echo -e "\\033[${wcol}G${welcome_message}"
echo -e "\\033[${icol}G${i_message}${NORMAL}"
echo ""
read -t "${itime}" -n 1 interactive 2>&1 > /dev/null
fi
# Make lower case
[ "${interactive}" == "I" ] && interactive="i"
[ "${interactive}" != "i" ] && interactive=""
# Read the state file if it exists from runlevel S
[ -r /var/run/interactive ] && source /var/run/interactive
# Attempt to stop all services started by the previous runlevel,
# and killed in this runlevel
if [ "${previous}" != "N" ]; then
for i in $(ls -v /etc/rc.d/rc${runlevel}.d/K* 2> /dev/null)
do
check_script_status
if [ "${SCRIPT_STAT}" == "1" ]; then
SCRIPT_STAT="0"
continue
fi
suffix=${i#/etc/rc.d/rc$runlevel.d/K[0-9][0-9]}
prev_start=/etc/rc.d/rc$previous.d/S[0-9][0-9]$suffix
sysinit_start=/etc/rc.d/rcS.d/S[0-9][0-9]$suffix
if [ "${runlevel}" != "0" -a "${runlevel}" != "6" ]; then
if [ ! -f ${prev_start} -a ! -f ${sysinit_start} ]; then
MSG="WARNING:\n\n${i} can't be "
MSG="${MSG}executed because it was not "
MSG="${MSG}not started in the previous "
MSG="${MSG}runlevel (${previous})."
log_warning_msg "$MSG"
continue
fi
fi
run ${i} stop
error_value=${?}
if [ "${error_value}" != "0" ]; then print_error_msg; fi
done
fi
if [ "${previous}" == "N" ]; then export IN_BOOT=1; fi
if [ "$runlevel" == "6" -a -n "${FASTBOOT}" ]; then
touch /fastboot
fi
# Start all functions in this runlevel
for i in $( ls -v /etc/rc.d/rc${runlevel}.d/S* 2> /dev/null)
do
if [ "${previous}" != "N" ]; then
suffix=${i#/etc/rc.d/rc$runlevel.d/S[0-9][0-9]}
stop=/etc/rc.d/rc$runlevel.d/K[0-9][0-9]$suffix
prev_start=/etc/rc.d/rc$previous.d/S[0-9][0-9]$suffix
[ -f ${prev_start} -a ! -f ${stop} ] && continue
fi
check_script_status
if [ "${SCRIPT_STAT}" == "1" ]; then
SCRIPT_STAT="0"
continue
fi
case ${runlevel} in
0|6)
run ${i} stop
;;
*)
run ${i} start
;;
esac
error_value=${?}
if [ "${error_value}" != "0" ]; then print_error_msg; fi
done
# Store interactive variable on switch from runlevel S and remove if not
if [ "${runlevel}" == "S" -a "${interactive}" == "i" ]; then
echo "interactive=\"i\"" > /var/run/interactive
else
rm -f /var/run/interactive 2> /dev/null
fi
# Copy the boot log on initial boot only
if [ "${previous}" == "N" -a "${runlevel}" != "S" ]; then
cat $BOOTLOG >> /var/log/boot.log
# Mark the end of boot
echo "--------" >> /var/log/boot.log
# Remove the temporary file
rm -f $BOOTLOG 2> /dev/null
fi
# End rc
EOF
Step 20 - Setup Basic Networking
The basic LFS network configuration uses a static IP. Most networks now use DHCP for network clients. This is slightly more complicated but we will need to perform this work eventually. I will provide both methods here, starting with the static version. If you have a static IP defined in your router it isn't a bad idea to set it up and check it first since it is the easiest. After it is working you can move on to the more complicated DHCP.
You will need to know what your networking device is named. You can find this in the host by typing
ifconfig in the terminal
$ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 10 bytes 580 (580.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10 bytes 580 (580.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlan0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 36:cd:9a:69:df:b9 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
This shows the local loopback (
lo) and my (currently unconnected) wireless called
wlan0. If you have a wired network connection, this is probably
eth0. If you are also using a wireless connection that uses WPA you'll have more work to do but we have installed
wpa-supplicant to handle it.
Static IP
The network script.
cd /etc/sysconfig/
cat > ifconfig.eth0 << "EOF"
ONBOOT=yes
IFACE=eth0
SERVICE=ipv4-static
IP=192.168.1.2
GATEWAY=192.168.1.1
PREFIX=24
BROADCAST=192.168.1.255
EOF
The
ONBOOT brings up the interface at boot.
IFACE is the network interface.
SERVICE setups the interface to use the static service (found in
/lib/services/ipv4-static) and sets the interface IP address to 192.168.1.2 with the gateway as 192.168.1.1. Change these to values that make sense for you. The
PREFIX value should be the number of bits used for the subnet. This is typically 24 for the 192.168.x.0/24 network (used by most home routers) where x can be from 0 to 255 with 0 and 1 being the most common.
Configure the Resolver
cat > /etc/resolv.conf << "EOF"
# Begin /etc/resolv.conf
domain my.local
nameserver 8.8.8.8
nameserver 8.8.4.4>
# End /etc/resolv.conf
EOF
This uses two Google public DNS servers and sets your domain to "my.local". Change these to proper values if you have them (such as a domain and local DNS server). It isn't necessary to have the domain entry if you are running a simple local network.
Setting the Hostname
echo "ttlp" > /etc/hostname
You can change this to whatever you want.
Creating the Initial Hosts File
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost
127.0.1.1 ttlp.local ttlp
192.168.1.1 ttlp.local ttlp keelbilled
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
This file is used to set aliases for systems. Instead of knowing the system's IP address, you simply can use the name. In the above file *ttlp.local* is the fully qualified domain name which for most systems is not a true FQDN because they are not on the Internet directly, but behind a firewall or screening router. The host name is
ttlp with an alias
keelbilled. Change all these values accordingly. If you have more than one system that has a known (static) IP you can add them in the form
<IP> <NAME> such as
192.168.1.2 server
192.168.1.3 proxybox
If you don't know all these values you'll need to talk to your network administrator but I'm assuming since this is most likely a home project, you'll find them in your router or router documentation.
DHCP with Wired or Open WIFI
We installed the
dhcpcd package which contains a DHCP client. It will handle BOOTP (RFC 951 and RFC 3315), IPv6 router solicitation (RFC 4861 and RFC 6106), and IPv6 privacy extensions (RFC 4941.) This is service that can be used to configure the network using a DHCP server. It functions by finding the DHCP server, negotiating a connection for the system, and running a series of "hook programs" which are programs that can be ran when DHCP brings up or shuts down a network interface. The daemon will check for their presence and run them if they are present.
Create the services directory
mkdir -vm755 /lib/services
Now create the dhcpcd services file for a standard LFS system. Like all LFS work, this script does what is supposed to do, only that, and does it very well.
cat > /lib/services/dhcpd << "EOF"
#!/bin/bash
# Begin services/dhcpcd
# Origianlly dased upon lfs-bootscripts-1.12 $NETWORK_DEVICES/if{down,up}
# Rewritten by Nathan Coulson <nathan@linuxfromscratch.org>
# Adapted for dhcpcd by DJ Lucas <dj@linuxfromscratch.org>
# Update for LFS 7.0 by Bruce Dubbs <bdubbs@linuxfromscratch,org>
# Call with: IFCONFIG=<filename> /lib/services/dhcpcd <IFACE> <up | down>
#$LastChangedBy: bdubbs $
#$Date: 2012-04-09 14:48:51 -0500 (Mon, 09 Apr 2012) $
. /lib/lsb/init-functions
. $IFCONFIG
pidfile="/var/run/dhcpcd-$1.pid"
case "$2" in
up)
log_info_msg "Starting dhcpcd on the $1 interface..."
# Test to see if there is a stale pid file
if [ -f "$pidfile" ]; then
ps `cat "$pidfile"` | grep dhcpcd > /dev/null
if [ $? != 0 ]; then
rm -f /var/run/dhcpcd-$1.pid > /dev/null
else
log_warning_msg "dhcpcd is already running!"
exit 2
fi
fi
/sbin/dhcpcd $1 $DHCP_START
evaluate_retval
;;
down)
log_info_msg "Stopping dhcpcd on the $1 interface..."
if [ -z "$DHCP_STOP" ]; then
killproc -p "${pidfile}" /sbin/dhcpcd
else
/sbin/dhcpcd $1 $DHCP_STOP &> /dev/null
if [ "$?" -eq 1 ]; then
log_warning_msg "dhcpcd not running!"
exit 2
fi
fi
evaluate_retval
;;
*)
echo "Usage: $0 [interface] {up|down}"
exit 1
;;
esac
# End services/dhcpcd
EOF
Now create the interface configuration file. This assumes your interface is *eth0* change it below if it is something different.
cat > /etc/sysconfig/ifconfig.eth0 << "EOF"
ONBOOT="yes"
IFACE="eth0"
SERVICE="dhcpcd"
DHCP_START="-b -q"
DHCP_STOP="-k"
EOF
Depending on the oddities of your network, you might need to add options to
DHCP_START and
DHCP_STOP. We will modify this script to add additional security later.
The dhcpcd service will setup your
/etc/resolv.conf based on the network you are using. One issue you might have with building a TTLP host, at least in the beginning, is that you might not have the networking component fully working and you'll need to boot into the host (MX Linux) to get the programs you need to fix it. The
chroot_to_lfs script will get you into the target, but the target won't have a working
/etc/resolv.conf since it wasn't configured by the local dhcpcd. To solve that problem copying the host's
/etc/resolv.conf to the target:
From a shell in the host
cp /etc/resolve.conf > $LFS/etc/resolv.conf
or to use a Google public DNS (so they can study where users are choosing to go)
echo "nameserver 8.8.8.8" > /etc/resolv.conf
Then use
chroot_to_lfs as usual.
The dhcpcd client will use several files to create the
/etc/resolv.conf. You can create a file called
/etc/resolv.conf.head that will be prepended to the DHCP server data, and another called
/etc/resolv.conf.tail that will appended to the DHCP server data similar to this
cat /etc/resolv.conf.head <from DHCP server> /etc/resolv.conf.tail > /etc/resolv.conf
You can use those files to set up whatever you might want, including overriding the DHCP server data or setting up a fail over.
WIFI with Encryption
For WiFi that uses a form of encryption (WPA, WPA2, WEP) we need more code. The
wpa_supplicant program can handle authentication to get setup on a encrypted network (wireless or not but we will focus on wireless since it is common.) Though it can handle WEP, you might as well just leave it open as I can break a WEP network in about twenty minutes with just my phone. We'll cover WPA-based networks.
First you need to create the configuration file. The supplied *wpa_passphrase* program will do this. To use it do the following
wpa_passphrase <SSID> <PASSWORD> > /etc/sysconfig/wpa_supplicant-<INTERFACE>.conf
where
<SSID> is the network ESSID,
<PASSWORD> is your password and
<INTERFACE> is the interface you are configuring (probably wlan0, maybe wifi0.)
An example is
wpa_passphrase netdecasa NotGood > /etc/sysconfig/wpa_supplicant-wlan0.conf
This will create the file with the following contents. You can also leave out the password and it will prompt for it if you need to keep it secret (and out of your command history file)
network={
ssid="netdecasa"
#psk="NotSoGood"
psk=96ad193569f50aeb008b3ed8b337cd5919bf74bb7fa116a7d6cadd6f8f522f56
}
As you can see, your password is in plain text in the file. This will be root owned file, but you probably don't want that password visible. Edit it out using an editor (such as
joe or
vim) or
sed -i 's/#pask="NotSoGood"//' /etc/syscnfig.wpa_supplicant-wlan0.conf
Unless you want to connect manually every time we need to do some more work to allow the interface to brought up on boot. First we need an LFS services script as before
cat > /lib/services/wpa << "EOF"
#!/bin/bash
# Begin services/wpa
# Origianlly based upon lfs-bootscripts-1.12 $NETWORK_DEVICES/if{down,up}
# Written by Armin K. <krejzi at email dot com>
# Call with: IFCONFIG=<filename> /lib/services/wpa <IFACE> <up | down>
#$LastChangedBy: bdubbs $
#$Date: 2016-09-02 23:10:02 -0500 (Fri, 02 Sep 2016) $
. /lib/lsb/init-functions
. $IFCONFIG
CFGFILE=/etc/sysconfig/wpa_supplicant-${IFCONFIG##*.}.conf
PIDFILE=/run/wpa_supplicant/$1.pid
CONTROL_IFACE=/run/wpa_supplicant/$1
case "$2" in
up)
if [ -e ${PIDFILE} ]; then
ps $(cat ${PIDFILE}) | grep wpa_supplicant >/dev/null
if [ "$?" = "0" ]; then
log_warning_msg "\n wpa_supplicant already running on $1."
exit 0
else
rm ${PIDFILE}
fi
fi
if [ ! -e ${CFGFILE} ]; then
log_info_msg "\n wpa_supplicant configuration file ${CFGFILE} not present"
log_failure_msg2
exit 1
fi
# Only specify -C on command line if it is not in CFGFILE
if ! grep -q ctrl_interface ${CFGFILE}; then
WPA_ARGS="-C/run/wpa_supplicant ${WPA_ARGS}"
fi
log_info_msg "\n Starting wpa_supplicant on the $1 interface..."
mkdir -p /run/wpa_supplicant
/sbin/wpa_supplicant -q -B -Dnl80211,wext -P${PIDFILE} \
-c${CFGFILE} -i$1 ${WPA_ARGS}
if [ "$?" != "0" ]; then
log_failure_msg2
exit 1
fi
log_success_msg2
if [ -n "${WPA_SERVICE}" ]; then
if [ ! -e /lib/services/${WPA_SERVICE} -a \
! -x /lib/services/${WPA_SERVICE} ]; then
log_info_msg "\n Cannot start ${WPA_SERVICE} on $1"
log_failure_msg2
exit 1
fi
IFCONFIG=${IFCONFIG} /lib/services/${WPA_SERVICE} $1 up
fi
;;
down)
if [ -n "${WPA_SERVICE}" ]; then
if [ ! -e /lib/services/${WPA_SERVICE} -a ! -x /lib/services/${WPA_SERVICE} ]; then
log_warning_msg "\n Cannot stop ${WPA_SERVICE} on $1"
else
IFCONFIG=${IFCONFIG} /lib/services/${WPA_SERVICE} $1 down
fi
fi
log_info_msg "\n Stopping wpa_supplicant on the $1 interface..."
if [ -e ${PIDFILE} ]; then
kill -9 $(cat ${PIDFILE})
rm -f ${PIDFILE} ${CONTROL_IFACE}
evaluate_retval
else
log_warning_msg "\n wpa_supplicant already stopped on $1"
exit 0
fi
;;
*)
echo "Usage: $0 [interface] {up|down}"
exit 1
;;
esac
# End services/wpa
EOF
Lastly, we need to setup the interface file in
/etc/sysconfig. If you are using an static IP you'll need to create the following
cat > /etc/sysconfig/ifconfig.wlan0 << "EOF"
ONBOOT="yes"
IFACE="wlan0"
SERVICE="wpa"
# Additional arguments to wpa_supplicant
WPA_ARGS=""
WPA_SERVICE="ipv4-static"
IP="192.168.1.1"
GATEWAY="192.168.1.2"
PREFIX="24"
BROADCAST="192.168.1.255"
EOF
But replace the
IP,
GATEWAY,
PREFIX, and
BROADCAST with your own values.
If you are using WPA with
dhcpcd then you'll need the following instead
cat > /etc/sysconfig/ifconfig.wlan0 << "EOF"
ONBOOT="yes"
IFACE="wlan0"
SERVICE="wpa"
# Additional arguments to wpa_supplicant
WPA_ARGS=""
WPA_SERVICE="dhcpcd"
DHCP_START="-b -q"
DHCP_STOP="-k"
EOF
Note this is exactly like the previous except it has the wpa_supplicant section added. The
ONBOOT="yes" will cause the network init script to bring the interface up when the system boots.
Step 21 - Setting Up the Clock
Your system clock is set to either use local time which has adjustments for daylight savings time or Coordinated Universal Time (UTC). The best solution is to use UTC unless you daul-boot Windows which will keep setting the clock back to local time. If you don't use Windows at all, use UTC.
To set to UTC do the following
cat > /etc/sysconfig/clock << EOF
# Begin /etc/sysconfig/clock
UTC=1
# Set this to any options you might need to give to hwclock,
# such as machine hardware clock type for Alphas.
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
If you need localtime use
cat > /etc/sysconfig/clock << EOF
# Begin /etc/sysconfig/clock
UTC=0
# Set this to any options you might need to give to hwclock,
# such as machine hardware clock type for Alphas.
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
The second part is to set the time zone data. There are two ways to do this, the easy way and the preferred way. The easy way is to use
tzselect. Here is what it looks like for me
$ tzselect
Please identify a location so that time zone rules can be set correctly.
Please select a continent, ocean, "coord", or "TZ".
1) Africa 5) Atlantic Ocean 9) Pacific Ocean
2) Americas 6) Australia 10) coord - I want to use geographical coordinates.
3) Antarctica 7) Europe 11) TZ - I want to specify the timezone using the Posix TZ format.
4) Asia 8) Indian Ocean
#? 2
Please select a country whose clocks agree with yours.
1) Anguilla 7) Belize 13) Chile 19) Dominican Republic 25) Guadeloupe 31) Martinique 37) Peru 43) St Martin (French) 49) United States
2) Antigua & Barbuda 8) Bolivia 14) Colombia 20) Ecuador 26) Guatemala 32) Mexico 38) Puerto Rico 44) St Pierre & Miquelon 50) Uruguay
3) Argentina 9) Brazil 15) Costa Rica 21) El Salvador 27) Guyana 33) Montserrat 39) St Barthelemy 45) St Vincent 51) Venezuela
4) Aruba 10) Canada 16) Cuba 22) French Guiana 28) Haiti 34) Nicaragua 40) St Kitts & Nevis 46) Suriname 52) Virgin Islands (UK)
5) Bahamas 11) Caribbean NL 17) Curaçao 23) Greenland 29) Honduras 35) Panama 41) St Lucia 47) Trinidad & Tobago 53) Virgin Islands (US)
6) Barbados 12) Cayman Islands 18) Dominica 24) Grenada 30) Jamaica 36) Paraguay 42) St Maarten (Dutch) 48) Turks & Caicos Is
#? 35
The following information has been given:
Panama
Therefore TZ='America/Panama' will be used.
Selected time is now: Fri Aug 16 15:19:07 EST 2019.
Universal Time is now: Fri Aug 16 20:19:07 UTC 2019.
Is the above information OK?
1) Yes
2) No
#? 1
You can make this change permanent for yourself by appending the line
TZ='America/Panama'; export TZ
to the file '.profile' in your home directory; then log out and log in again.
Here is that TZ value again, this time on standard output so that you
can use the /usr/bin/tzselect command in shell scripts:
America/Panama
This is fine and will set the time zone accordingly. This will set the system default by copying the zone info file to
/etc/localtime which hides the zone. It is better to link the zone info file to
/etc/localtime to easily see the current setting.
First look in
/usr/share/zoneinfo
$ ls /usr/share/zoneinfo/
Africa CST6CDT GMT Israel NZ-CHAT Singapore
America Cuba GMT+0 Jamaica Pacific Turkey
Antarctica EET GMT-0 Japan Poland UCT
Arctic Egypt GMT0 Kwajalein Portugal Universal
Asia Eire Greenwich Libya posix US
Atlantic EST Hongkong MET posixrules UTC
Australia EST5EDT HST Mexico PRC WET
Brazil Etc Iceland MST PST8PDT W-SU
Canada Europe Indian MST7MDT right zone1970.tab
CET GB Iran Navajo ROC zone.tab
Chile GB-Eire iso3166.tab NZ ROK Zulu
The inside my zone directory is
America
$ ls /usr/share/zoneinfo/America/
Adak Coral_Harbour Havana Miquelon Rosario
Anchorage Cordoba Hermosillo Moncton Santa_Isabel
Anguilla Costa_Rica Indiana Monterrey Santarem
Antigua Creston Indianapolis Montevideo Santiago
Araguaina Cuiaba Inuvik Montreal Santo_Domingo
Argentina Curacao Iqaluit Montserrat Sao_Paulo
Aruba Danmarkshavn Jamaica Nassau Scoresbysund
Asuncion Dawson Jujuy New_York Shiprock
Atikokan Dawson_Creek Juneau Nipigon Sitka
Atka Denver Kentucky Nome St_Barthelemy
Bahia Detroit Knox_IN Noronha St_Johns
Bahia_Banderas Dominica Kralendijk North_Dakota St_Kitts
Barbados Edmonton La_Paz Ojinaga St_Lucia
Belem Eirunepe Lima Panama St_Thomas
Belize El_Salvador Los_Angeles Pangnirtung St_Vincent
Blanc-Sablon Ensenada Louisville Paramaribo Swift_Current
Boa_Vista Fortaleza Lower_Princes Phoenix Tegucigalpa
Bogota Fort_Nelson Maceio Port-au-Prince Thule
Boise Fort_Wayne Managua Porto_Acre Thunder_Bay
Buenos_Aires Glace_Bay Manaus Port_of_Spain Tijuana
Cambridge_Bay Godthab Marigot Porto_Velho Toronto
Campo_Grande Goose_Bay Martinique Puerto_Rico Tortola
Cancun Grand_Turk Matamoros Punta_Arenas Vancouver
Caracas Grenada Mazatlan Rainy_River Virgin
Catamarca Guadeloupe Mendoza Rankin_Inlet Whitehorse
Cayenne Guatemala Menominee Recife Winnipeg
Cayman Guayaquil Merida Regina Yakutat
Chicago Guyana Metlakatla Resolute Yellowknife
Chihuahua Halifax Mexico_City Rio_Branco
There I find the file
/usr/share/zoneinfo/America/Panama. To set this as the local timezone
rm /etc/localtime
ln -s /usr/share/zoneinfo/America/Panama /etc/localtime
The time zone is now set to mine. Be sure to choose yours for this step.
Step 22 - Shell Setup
We will use the BLFS shell startup scripts which create the standard !/etc/profile.d! directory to allow easy changes to the shell startup scripts.
There are several different scripts that I find few people, even long time Linux users, understand. Shells are either interactive or non-interactive. An interactive shells is one that is started for command line input such as issuing
/bin/bash. It will start, initialize, provide a prompt, and wait for commands. When a command is entered, it is executed, and when it completes another prompt is given. An non-interactive shell is one that is started to run a script, such as
/bin/bash mount_all. This shell only exists to run the
mount_all program and the shell stops executing upon completion. The Bash shell (short for Bourne-Again Shell) can be started as
/bin/bash or
/bin/sh if it is linked to this name.
The login program will start the first shell in interactive mode, called the login shell, and the shell will look for
/etc/profile for initialization and then look in the user's home directory for a file called
~/.bash_profile if invoked as
/bin/bash or
~/.profile if invoked as
/bin/sh. When a non-login interactive shell is started such as
/bin/bash at the command line or a graphical terminal emulator like
xterm it copies the parent's environment and looks for a file in the home directory called
~/.bashrc for an additional setup; the interactive login shell ignores this file. A non-interactive shell copies its parent environment and doesn't read any initialization scripts.
Many distros will have a file called
/etc/bashrc as a default to setup non-interactive shells, but Bash will not look for this itself so the distro creators much source it in the user's
~/.bashrc.
First create the master script that will initialize
cat > /etc/profile << "EOF"
# Begin /etc/profile
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# modifications by Dagmar d'Surreal <rivyqntzne@pbzpnfg.arg>
# System wide environment variables and startup programs.
# System wide aliases and functions should go in /etc/bashrc. Personal
# environment variables and startup programs should go into
# ~/.bash_profile. Personal aliases and functions should go into
# ~/.bashrc.
# Functions to help us manage paths. Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
local IFS=':'
local NEWPATH
local DIR
local PATHVARIABLE=${2:-PATH}
for DIR in ${!PATHVARIABLE} ; do
if [ "$DIR" != "$1" ] ; then
NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
fi
done
export $PATHVARIABLE="$NEWPATH"
}
pathprepend () {
pathremove $1 $2
local PATHVARIABLE=${2:-PATH}
export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}
pathappend () {
pathremove $1 $2
local PATHVARIABLE=${2:-PATH}
export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}
export -f pathremove pathprepend pathappend
# Set the initial path
export PATH=/bin:/usr/bin
if [ $EUID -eq 0 ] ; then
pathappend /sbin:/usr/sbin
unset HISTFILE
fi
# Setup some environment variables.
export HISTSIZE=1000
export HISTIGNORE="&:[bf]g:exit"
# Set some defaults for graphical systems
export XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share/}
export XDG_CONFIG_DIRS=${XDG_CONFIG_DIRS:-/etc/xdg/}
export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/xdg-$USER}
# Setup a red prompt for root and a green one for users.
NORMAL="\[\e[0m\]"
RED="\[\e[1;31m\]"
GREEN="\[\e[1;32m\]"
if [[ $EUID == 0 ]] ; then
PS1="$RED\u [ $NORMAL\w$RED ]# $NORMAL"
else
PS1="$GREEN\u [ $NORMAL\w$GREEN ]\$ $NORMAL"
fi
for script in /etc/profile.d/*.sh ; do
if [ -r $script ] ; then
. $script
fi
done
unset script RED GREEN NORMAL
# End /etc/profile
EOF
This creates some handy functions to modify the path that will be used in the scripts called pathremove (to remove something from
PATH), pathprepend (to add something to the beginning of
PATH), and pathappend (to put something on the end of
PATH.) It sets an initial path and adds the secure binary areas (
/sbin, /usr/sbin) if the user is root. It sets up the shell to remember the last 1,000 commands you typed (
HISTSIZE). Creates some environment variables for open desktop standards we will discuss later. Then adds a prompt that is red for root or green otherwise. Notice at the bottom this script will source all scripts found in
/etc/profile.d which is an easy way to add to the default profile.
It is easy to add the contents of a script to the current shell. Keep in mind if you simply execute a script as in
~/.bash_profile
it wouldn't work as that is not an executable script. Even if it were it is executed in a separate shell and would effect only that shell. The
source command adds the contents of a script to the current shell's environment. It is done like this
source ~/.bash_profile
or simply
. ~/.bash_profile
Make the
profile.d directory
mkdir -m 0755 /etc/profile.d
Create the system default profile
cat > /etc/bashrc << "EOF"
# Begin /etc/bashrc
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# updated by Bruce Dubbs <bdubbs@linuxfromscratch.org>
# System wide aliases and functions.
# System wide environment variables and startup programs should go into
# /etc/profile. Personal environment variables and startup programs
# should go into ~/.bash_profile. Personal aliases and functions should
# go into ~/.bashrc
# Provides colored /bin/ls and /bin/grep commands. Used in conjunction
# with code in /etc/profile.
alias ls='ls --color=auto'
alias grep='grep --color=auto'
# Provides prompt for non-login shells, specifically shells started
# in the X environment. [Review the LFS archive thread titled
# PS1 Environment Variable for a great case study behind this script
# addendum.]
NORMAL="\[\e[0m\]"
RED="\[\e[1;31m\]"
GREEN="\[\e[1;32m\]"
if [[ $EUID == 0 ]] ; then
PS1="$RED\u [ $NORMAL\w$RED ]# $NORMAL"
else
PS1="$GREEN\u [ $NORMAL\w$GREEN ]\$ $NORMAL"
fi
unset RED GREEN NORMAL
# End /etc/bashrc
EOF
This creates aliases to turn on the color for
ls and
grep and sets the prompt.
Create the user default
.bash_profile in
/etc/skel
cat > /etc/skel/.bash_profile << "EOF"
# Begin ~/.bash_profile
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# updated by Bruce Dubbs <bdubbs@linuxfromscratch.org>
# Personal environment variables and startup programs.
# Personal aliases and functions should go in ~/.bashrc. System wide
# environment variables and startup programs are in /etc/profile.
# System wide aliases and functions are in /etc/bashrc.
if [ -f "$HOME/.bashrc" ] ; then
source $HOME/.bashrc
fi
if [ -d "$HOME/bin" ] ; then
pathprepend $HOME/bin
fi
# End ~/.bash_profile
EOF
Files in
/etc/skel will be copied to each user's directory if you create user accounts with
useradd -m program. The default user scripts sources the user's
.bashrc if it exists, and adds the user's bin directory
Create the user
.profile
cat > /etc/skel/.profile << "EOF"
# Begin ~/.profile
# Personal environment variables and startup programs.
if [ -d "$HOME/bin" ] ; then
pathprepend $HOME/bin
fi
# Set up user specific i18n variables
#Users change this to override system
#export LANG=<ll>_<CC>.<charmap><@modifiers>
# End ~/.profile
EOF
The
.profile is a "generic" shell initialization. It should keep things simple and none Bash specific because
/bin/sh might not be Bash. For this reason, they are separate files. There should be no Bash specific features. Common shells that distros use that are based on the original Bourne shell are
dash (a simplified form of Bash),
ksh (The Korn shell by David G. Korn),
fish (friendly interactive), and the
zsh (The Z Shell.)
Make the user
.bashrc
cat > /etc/skel/.bashrc << "EOF"
# Begin ~/.bashrc
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# Personal aliases and functions.
# Personal environment variables and startup programs should go in
# ~/.bash_profile. System wide environment variables and startup
# programs are in /etc/profile. System wide aliases and functions are
# in /etc/bashrc.
if [ -f "/etc/bashrc" ] ; then
source /etc/bashrc
fi
# Set up user specific i18n variables
# User change this to override system default
#export LANG=<ll>_<CC>.<charmap><@modifiers>
# End ~/.bashrc
EOF
This only sources the system
bashrc in
/etc and gives the user a template for changing the i18n language setting.
Create a template for
.bash_logout that users can extend if necessary. This will be ran on logout by the login interactive shell. Most users would clear the screen with
clear but that functionality has now moved to
/etc/issue. This is a good place to clear your own local temporary directory (
~/tmp) if you choose.
cat > /etc/skel/.bash_logout << "EOF"
# Begin ~/.bash_logout
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# Personal items to perform on logout.
# End ~/.bash_logout
EOF
These files need to be set for the root user which we have already created
cp /etc/skel/.* ~
chown root:root ~/.bash* ~/.profile
chmod 600 ~/.bash* ~/.profile
Step 23 - Directory Colors
Now set some nice color settings for directory listings
cat > /etc/profile.d/dircolors.sh << "EOF"
# Setup for /bin/ls and /bin/grep to support color, the alias is in /etc/bashrc.
if [ -f "/etc/dircolors" ] ; then
eval $(dircolors -b /etc/dircolors)
fi
if [ -f "$HOME/.dircolors" ] ; then
eval $(dircolors -b $HOME/.dircolors)
fi
alias ls='ls --color=auto'
alias grep='grep --color=auto'
EOF
And create the colors file
dircolors -p > /etc/dircolors
You can edit these if you choose. See the header of the file.
Step 24 - Locale Settings
The locale settings determine the language used on the computer. Some applications will require "language packs" (such as LibreOffice) to convert a program fully to a different language than the one chosen on install, and many don't have an translations at all. But the language settings still determine various items such as date and time format, currency symbol, default page size, etc.
To see what local are available run
locale -a
This will print a list of the locales installed. There are usually two character encodings ISO-8859x and UTF-8. UTF-8 is required for many programs to allow the use of Unicode. It is the preferred method though not all programs yet support it-- the majority do and it's a large majority. Use the UTF-8 mapping if possible.
This will list a series of files that start with a language abbreviation such as "en" for English, "es" for Spanish (espanol), "it" for Italian, etc.
Find the language in the local list and test it using the following by replacing
<locale name> with the locale you need
LC_ALL=<locale name> locale language
LC_ALL=<locale name> locale charmap
LC_ALL=<locale name> locale int_curr_symbol
LC_ALL=<locale name> locale int_prefix
If you encounter errors you'll need to use
localdef to create the proper locale or find one on the Internet. If all goes well then you can set default system locale using the LANG environment variable
Once you have the locale, set it using
/etc/profile/i18n.sh
cat > /etc/profile.d/i18n.sh << "EOF"
# Set up i18n variables
export LANG="en_US.utf8"
EOF
Be sure to change the "en_US.utf8" to the proper value.
Step 25 - Setting Up Readline
Maybe you've noticed that many programs allow you to use all the editing keys you have available to you in the bash shell such as jump to beginning, jump to end, even search. This comes from the
readline library which make command line input standard for any program. It uses a file called
inputrc to configure it. The install for readline created
/etc/inputrc which is the system default. But users may want to create their own using
~/.inputc. The environment variable
INPUTRC tells readline what file to use. This script will change it to the system default if the user script isn't present.
cat > /etc/profile.d/readline.sh << "EOF"
# Setup the INPUTRC environment variable.
if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ] ; then
INPUTRC=/etc/inputrc
fi
export INPUTRC
EOF
Step 26 -Setting up Security
This value sets the file creation mask. As a mask it removes the specified bits. If you're not fully versed on Unix permissions, don't worry that will be covered later. Below sets the mask to remove the public read, write, and execute bits except when the user and group aren't the same value then it also removes the group write permission. This ensures that files users create can't see read, written, or executed by all other public users. This is more restrictive than the standard which is to allow all users to read files created and list the contents of created directories. In this case a user needs to purposefully mark a file as publicly readable or writable if they want users to read or change their files.
cat > /etc/profile.d/umask.sh << "EOF"
# By default, the umask should be set.
if [ "$(id -gn)" = "$(id -un)" -a $EUID -gt 99 ] ; then
umask 007
else
umask 027
fi
EOF
The
shells file contains the shells that are considered valid for login. If the user wants to change his login shell so something else, it must be allowed here. This is an added step for security. Right now with only
bash installed, that is all we will allow
cat > /etc/shells << EOF
# Begin /etc/shells
/bin/sh
/bin/bash
# End /etc/shells
EOF
Step 27 - Bash Completion
Bash can be configured to perform completion with the <TAB> key for various common commands. These are listed in
/usr/share/bash-completion/completions. If these are added to the shell, it will intelligently allow completion of required options and selective valid files based on the command. This can be very nice, but it makes the output of the
set command very long and some don't like this. This step is optional, but recommended for a multi-user system.
cat > /etc/profile.d/bash_completion.sh << "EOF"
# Begin /etc/profile.d/bash_completion.sh
# Import bash completion scripts
# If the bash-completion package is installed, use its configuration instead
if [ -f /usr/share/bash-completion/bash_completion ]; then
# Check for interactive bash and that we haven't already been sourced.
if [ -n "${BASH_VERSION-}" -a -n "${PS1-}" -a -z "${BASH_COMPLETION_VERSINFO-}" ]; then
# Check for recent enough version of bash.
if [ ${BASH_VERSINFO[0]} -gt 4 ] || \
[ ${BASH_VERSINFO[0]} -eq 4 -a ${BASH_VERSINFO[1]} -ge 1 ]; then
[ -r "${XDG_CONFIG_HOME:-$HOME/.config}/bash_completion" ] && \
. "${XDG_CONFIG_HOME:-$HOME/.config}/bash_completion"
if shopt -q progcomp && [ -r /usr/share/bash-completion/bash_completion ]; then
# Source completion code.
. /usr/share/bash-completion/bash_completion
fi
fi
fi
else
# bash-completions are not installed, use only bash completion directory
if shopt -q progcomp; then
for script in /etc/bash_completion.d/* ; do
if [ -r $script ] ; then
. $script
fi
done
fi
fi
# End /etc/profile.d/bash_completion.sh
EOF
Make the directory for additions if the full file is not available
mkdir -m 0755 /etc/bash_completion.d
Now you have two choices. One, add all installed commands to the completion list, or select only those you are interested in. Let's start by looking at the scripts.
$ ls /usr/share/bash-completion/completions/
addpart fsck lsmem rtcwake
blkdiscard fsck.cramfs lsns script
blkid fsck.minix mcookie scriptreplay
blkzone fsfreeze mesg setarch
blockdev fstrim mkfs setsid
cal getopt mkfs.bfs setterm
cfdisk grub mkfs.cramfs sfdisk
chcpu hexdump mkfs.minix swaplabel
chmem hwclock mkswap swapoff
chrt ionice more swapon
col ipcmk mount taskset
colcrt ipcrm mountpoint tc
colrm ipcs namei ul
column isosize ninja umount
ctrlaltdel kmod nsenter unshare
delpart last partx utmpdump
dmesg ldattach pivot_root uuidd
eject logger prlimit uuidgen
fallocate look raw uuidparse
fdformat losetup readprofile wall
fdisk lsblk rename wdctl
fincore lscpu renice whereis
findfs lsipc resizepart wipefs
findmnt lslocks rev zramctl
flock lslogins rfkille
This shows scripts for the commands that have been installed in the system so far. More will be added as you add more packages. Each package may set the completion for the given command so that when you hit <TAB> it will either print a list of options, or start them with the appropriate text. Each these will add to your environment for bash completion. Fortunately, *bash* can now do this dynamically if setup properly. Just for fun, try this
$ cat *|wc -c
133627
That show so far it would take more than 128K to hold all the completions in memory. That's eight times more memory than the first computer I owned! Luckily today that's nothing to the gigabytes we have available in RAM.
You can copy the commands you use most of the time or you can just let Bash see them all and add them as you use them. If you want to selectively add them, copy (or symbolically link) the files to
/etc/bash_completion.d/. Or install the
bash_completion script in
/usr/share/bash_completion/. The script comes with Bash but is not installed by default. You can create it easily.
cd /sources
tar -xf bash-5.0.tar.gz
cd bash-5.0/examples/bash-completion/
tar -xf bash-completion-2.5.tar.xz
cd bash-completion-2.5
./configure --prefix=/usr && make && make install
cd /sources
rm -rf bash-5.0
That will add everything necessary for the completion as provided by bash which is 728 commands for a total of roughly 256K. I chose not to add those to the configuration and install of
bash but we will later create a separate package to do this as part of the package manager to keep the completion separate from the shell. I don't use completion beyond directories and files as I know the commands I use every day by heart and use the man pages for everything else. For beginners and learners, it's not a bad idea to enable command completion.
Step 28 - Setting up the Console
We will configure the console to use the framebuffer for the graphic console. This often makes the screen very difficult to read. My own screen defaults to 3,840 by 2,160 pixels which makes the console unreadable to me (time spent staring at a computer screen most the day has finally caught up to me). For now we will install a default font that will make this easier to read, and later install more choices from the Terminus fonts package. You might also need a custom keymap.
Those are controlled using the
/etc/sysconfig/console file. For now we'll set the keymap (if you know it) and the font and leave the other work for later. Since this will be covered comprehensively later, this will be short.
cat > /etc/sysconfig/ < EOF
UNICODE=1
FONT=sun12x22
EOF
If you need a different keymap and you know what it is, you'll need the following
cat > /etc/sysconfig/ < EOF
UNICODE=1
FONT=sun12x22
KEYMAP=es
EOF
The keymap files are found in
/usr/share/keymaps and are loaded with the
loadkeys program.
Step 29 - The rc.site file
All the settings supported by the LFS init scripts are put in a file called
/etc/sysconfig/rc.site. It will first check there for any settings, set them, and then use the other scripts in
/etc/sysconfig as overrides. You can configure either way, but it is often easier to create a single file from the command line than to edit one. Here's what it looks like
# rc.site
# Optional parameters for boot scripts.
# Distro Information
# These values, if specified here, override the defaults
#DISTRO="Linux From Scratch" # The distro name
#DISTRO_CONTACT="lfs-dev@linuxfromscratch.org" # Bug report address
#DISTRO_MINI="LFS" # Short name used in filenames for distro config
# Define custom colors used in messages printed to the screen
# Please consult `man console_codes` for more information
# under the "ECMA-48 Set Graphics Rendition" section
#
# Warning: when switching from a 8bit to a 9bit font,
# the linux console will reinterpret the bold (1;) to
# the top 256 glyphs of the 9bit font. This does
# not affect framebuffer consoles
# These values, if specified here, override the defaults
#BRACKET="\\033[1;34m" # Blue
#FAILURE="\\033[1;31m" # Red
#INFO="\\033[1;36m" # Cyan
#NORMAL="\\033[0;39m" # Grey
#SUCCESS="\\033[1;32m" # Green
#WARNING="\\033[1;33m" # Yellow
# Use a colored prefix
# These values, if specified here, override the defaults
#BMPREFIX=" "
#SUCCESS_PREFIX="${SUCCESS} * ${NORMAL}"
#FAILURE_PREFIX="${FAILURE}*****${NORMAL}"
#WARNING_PREFIX="${WARNING} *** ${NORMAL}"
# Manually set the right edge of message output (characters)
# Useful when resetting console font during boot to override
# automatic screen width detection
#COLUMNS=120
# Interactive startup
#IPROMPT="yes" # Whether to display the interactive boot prompt
#itime="3" # The amount of time (in seconds) to display the prompt
# The total length of the distro welcome string, without escape codes
#wlen=$(echo "Welcome to ${DISTRO}" | wc -c )
#welcome_message="Welcome to ${INFO}${DISTRO}${NORMAL}"
# The total length of the interactive string, without escape codes
#ilen=$(echo "Press 'I' to enter interactive startup" | wc -c )
#i_message="Press '${FAILURE}I${NORMAL}' to enter interactive startup"
# Set scripts to skip the file system check on reboot
#FASTBOOT=yes
# Skip reading from the console
#HEADLESS=yes
# Write out fsck progress if yes
#VERBOSE_FSCK=no
# Speed up boot without waiting for settle in udev
#OMIT_UDEV_SETTLE=y
# Speed up boot without waiting for settle in udev_retry
#OMIT_UDEV_RETRY_SETTLE=yes
# Skip cleaning /tmp if yes
#SKIPTMPCLEAN=no
# For setclock
#UTC=1
#CLOCKPARAMS=
# For consolelog (Note that the default, 7=debug, is noisy)
#LOGLEVEL=7
# For network
#HOSTNAME=mylfs
# Delay between TERM and KILL signals at shutdown
#KILLDELAY=3
# Optional sysklogd parameters
#SYSKLOGD_PARMS="-m 0"
# Console parameters
#UNICODE=1
#KEYMAP="de-latin1"
#KEYMAP_CORRECTIONS="euro2"
#FONT="lat0-16 -m 8859-15"
#LEGACY_CHARSET=
Step 30 - Setting Up the Login Screen
We will modify the login screen to show a custom login prompt. This comes from a file called
/etc/issue
First set it to clear the screen.
clear > /etc/issue
The modify it for with the TTLP login
cat >> /etc/issue < EOF
,-,---.
__/( ,----`
_,-' ;
;;.---..-'
""
Toucan Linux on \s \m running Linux \r \v
Authorized Users Only
EOF
If you choose to edit this file leave the first line alone to be sure to clear the console.
Step 31 - Naming the Distro
We will name the distro for The Toucan Linux Project. We'll create a file in
/etc/sysconfig called
distro that will change the name.
cat > /etc/sysconfig/distro << "EOF"
DISTRO="The Toucan Linux Project" # The distro name
DISTRO_CONTACT="the_toucan_linux_project@gmail.com" # Bug report address
DISTRO_MINI="TTLP" # Short name used in filenames for distro config
EOF
In strict LFS you need to do this by editing the
rc.site file. We modified the
rc script to allow it to be done easily. Why? Because this is your distro. Change it to what you want.
Step 32 - Interactive Startup
The last step is to enable the interactive startup of the scripts. This will give a prompt and allow you three seconds to enable the interactive startup in which each step of the boot process will need to be verified by a user on the console by answer 'Y' to run it or 'N' to skip it. This is a great debugging tool that does slow down the boot, but for at least now, we want it on so we have the ability to check our work. Later we can delete this file.
cat > /etc/sysconfig/interactive << "EOF"
IPROMPT="yes" # Whether to display the interactive boot prompt
itime="3" # The amount of time (in seconds) to display the prompt
EOF
That's it. It's time to create a kernel and make the target bootable. We'll save the work for a future article.
Copyright (C) 2019 by Michael R Stute