Wednesday, August 21, 2019

9 - Final Build and Configuration





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

No comments:

Post a Comment