Skip to content

2. System Design

D3vil0p3r edited this page Nov 13, 2022 · 16 revisions

Modes

Athena OS is based on Arch Linux. At the beginning of the project, we started from an Arch Linux ISO and built Athena step by step, tailoring it with functional and non-functional requirements.

The steps involved for the creation of the base of Athena are shown. During the development phase, Athena has been built in two different ways:

  • Direct: Arch Linux was used as base and built manually with only modules we need
  • ISO: Arch Linux was used as base and built automatically with only modules we need. The automation has been reached by using Archiso

Direct Approach

Create a Virtual Machine with at least 80 GB of disk space.

Enable EFI

In case we are using VirtualBox, enable EFI by the Virtual Machine settings we create:

  • Click on your Virtual Machine label of VirtualBox
  • Click on Settings -> System -> Motherboard -> Enable EFI Don't enable 3D Acceleration on VirtualBox because bugged.

In case we are using VMware, create a new Virtual Machine by selecting I will install the operating system later.

Go to the VMware Virtual Machine folder we just created, edit the .vmx file by a text editor and add the following line on the 2nd row:

firmware = "efi"

Run the Virtual Machine and the screen will stop on some messages related EFI loading. Go above on Player menu -> Removable Devices -> CD/DVD (IDE) -> Settings -> Select the Arch Linux .iso. Then again, Player menu -> Removable Devices -> CD/DVD (IDE) -> Connect and wait some minutes and we should get the access to the EFI Boot Manager. Choose the 2nd voice EFI VMWare Virtual IDE CDROM Drive (IDE 1:0) and we get the Arch Boot Window for UEFI. Furthermore, we can enable Accelerate 3D graphics by the Virtual Machine settings.

Setting up the disk (with UEFI)

Get the list of disks and their size:

$ fdisk -l

Let's guess we get /dev/sda.

For setting the disk:

fdisk /dev/sda

Command (m for help): p

Command (m for help): g

Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-xxxxxx, default 2048):
Last sector, +/- sectors or +/-size{K,M,G,T,P} (2048-xxxxx, default xxxxx): +500M

Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.

Command (m for help): p

Command (m for help): n
Partition number (2-128, default 2):
First sector (xxx-xxxxxx, default xxx):
Last sector, +/- sectors or +/-size{K,M,G,T,P} (xxx-xxxxx, default xxxxx):

Command (m for help): t
Partition number (1-2, default 2):
Partition type or alias (type L to list all): 43
Changed type of partition 'Linux filesystem' to Linux LVM'.

Command (m for help): p
[Now we see two partitions: EFI System (/dev/sda1) and Linux LVM (/dev/sda2)]

Command (m for help): w

Now we wrote the changes. Here we will create two logical volumes: lv_root and lv_home but if you allocated not much space and want to avoid any disk space issues, create only one logical volume.

$ mkfs.fat -F32 /dev/sda1

$ pvcreate --dataalignment 1m /dev/sda2

$ vgcreate volgroup0 /dev/sda2

$ lvcreate -L 50GB volgroup0 -n lv_root

$ lvcreate -l 100%FREE volgroup0 -n lv_home

$ modprobe dm_mod

$ vgscan

$ vgchange -ay

$ mkfs.ext4 /dev/volgroup0/lv_root

$ mount /dev/volgroup0/lv_root /mnt

$ mkfs.ext4 /dev/volgroup0/lv_home

$ mkdir /mnt/home

$ mount /dev/volgroup0/lv_home /mnt/home

$ mkdir /mnt/etc

$ genfstab -U -p /mnt >> /mnt/etc/fstab

$ cat /mnt/etc/fstab #Check if there are no errors

Install Arch Linux

We just set the partition. Now we must install Arch Linux that will be our base for Athena:

$ pacstrap -i /mnt base

$ arch-chroot /mnt

$ pacman -S linux linux-headers

OR

$ pacman -S linux-lts linux-lts-headers

OR install all of them. Let's guess to install linux and linux-headers:

$ pacman -S nano

$ pacman -S base-devel openssh

OpenSSH is optional. If you install it, be sure to run systemctl enable sshd after the installation.

$ pacman -S networkmanager wpa_supplicant wireless_tools netctl inetutils

$ pacman -S dialog

$ systemctl enable NetworkManager

$ pacman -S lvm2

$ nano /etc/mkinitcpio.conf

Scroll until the first uncommented HOOKS=(base udev autodetect modconf block filesystems keyboard fsck) and add lvm2 as: HOOKS=(base udev autodetect modconf block lvm2 filesystems keyboard fsck). Note that, if btrfs will be used as filesystem, fsck hook can be removed because no needed (source: https://wiki.archlinux.org/title/Improving_performance/Boot_process#Filesystem_mounts).

$ mkinitcpio -p linux

If you installed linux-lts instead of linux, run mkinitcpio -p linux-lts.

$ nano /etc/locale.gen

Uncomment en_US.UTF-8 UTF-8.

$ nano /etc/locale.conf

Add LANG=en_US.UTF-8.

$ locale-gen

$ passwd
Enter root password

$ useradd -m -g users -G wheel <your-username>

$ passwd <your-username>
Enter <your-username> password

$ pacman -S sudo

$ EDITOR=nano visudo

Scroll down and uncomment # %wheel ALL=(ALL) ALL and save the file.

Install GRUB

Let's install GRUB as bootloader of our OS:

$ pacman -S grub dosfstools os-prober mtools

$ pacman -S efibootmgr #If you are using UEFI partition

$ mkdir /boot/EFI

$ mount /dev/sda1 /boot/EFI

$ grub-install --target=x86_64-efi --bootloader-id=grub_uefi --recheck # It works in case of UEFI partition

$ ls -l /boot/grub
Check if "locale" folder exists. If does not, mkdir /boot/grub/locale

$ cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale/en.mo #en is related to language

$ pacman -S intel-ucode # if you have AMD, install amd-ucode

$ grub-mkconfig -o /boot/grub/grub.cfg

$ exit

$ umount -a

$ reboot

If you are on VirtualBox, save a snapshot of the Virtual Machine. If you are on VMware Workstation Player, make a backup of the folder containing your Virtual Machine files.

Post-Install Tweaks

We proceed to install the Swap partition and some drivers (e.g., NVIDIA)

$ su
Enter root password

$ cd /root

$ dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress

$ chmod 600 /swapfile

$ mkswap /swapfile

$ cp /etc/fstab /etc/fstab.bak

$ echo '/swapfile none swap sw 0 0' | tee -a /etc/fstab

$ cat /etc/fstab
# Check if the line is inserted correctly

$ free -m

$ mount -a

$ free -m

$ swapon -a

$ free -m

$ timedatectl set-timezone Europe/Zurich

$ systemctl enable systemd-timesyncd

$ hostnamectl set-hostname <your-hostname>

$ nano /etc/hosts
# Add:
127.0.0.1 localhost
127.0.1.1 <your-hostname>

$ pacman -S xorg-server # I don't install it

$ pacman -S nvidia # if we installed linux-lts package at the beginning, install nvidia-lts. If you don't have NVIDIA, install mesa package

$ pacman -S wget which git man-db man-pages

$ mandb # command to generate the search database for manpage entries.

# Check if unset MANPATH so that mandb could use the man_db.conf (check also the content of man_db.conf for checking what it contains)

Note that generally NVIDIA drivers don't work in VirtualBox and VMware Workstation Player environment. You cannot install drivers for the Host's Graphics Adapter in a Virtual Machine because GPU passthrough is not available on VM softwares. So you are always using a virtual graphics adapter and not the one installed on your host OS. The Virtual Machine does not see your GPU and uses its own Drivers when VMware Tools or VirtualBox Guest Additions are installed.

Now, let's install Virtual Machine tools. If we are working on WMware, install:

$ pacman -S open-vm-tools

$ systemctl enable vmtoolsd.service

$ systemctl enable vmware-vmblock-fuse.service

If we are working on VirtualBox:

$ pacman -S virtualbox-guest-utils xf86-video-vmware # You need both of them if you are using VirtualBox

$ systemctl enable vboxservice

Install GNOME

Athena is based on GNOME as Desktop Environment because of the usage of particular GNOME extensions. The GNOME package will install also GDM display manager. As display protocol, we will use Wayland:

$ pacman -S gnome

$ pacman -S gnome-tweaks wayland

$ systemctl enable gdm # Enabling this, it allows the login window to appear at startup. GDM is the display manager of GNOME

$ reboot

For native installation of Athena where you can use NVIDIA drivers, you can follow these links for better integrating NVIDIA with Wayland:

Source: https://www.youtube.com/watch?v=DPLnBPM4DhI&ab_channel=LearnLinuxTV

Install GNOME Extensions

Fly-Pie

wget https://github.com/Schneegans/Fly-Pie/releases/latest/download/[email protected]

gnome-extensions install [email protected]

Logout / Login since we are on Wayland.

gnome-extensions enable [email protected]

Test it by pressing CTRL+SPACE on the keyboard.

For accessing to the settings:

gnome-extensions prefs [email protected]

For exporting your custom settings, use:

dconf dump /org/gnome/shell/extensions/flypie/ > dconf-flypie.ini

For importing your custom settings, use:

dconf load /org/gnome/shell/extensions/flypie/ < dconf-flypie.ini

Burn My Windows

wget https://github.com/Schneegans/Burn-My-Windows/releases/latest/download/[email protected]

gnome-extensions install [email protected]

Logout / Login since we are on Wayland.

gnome-extensions enable [email protected]

Test it by opening and closing a window.

For accessing to the settings:

gnome-extensions prefs [email protected]

For exporting your custom settings, use:

dconf dump /org/gnome/shell/extensions/burn-my-windows/ > dconf-bmw.ini

For importing your custom settings, use:

dconf load /org/gnome/shell/extensions/burn-my-windows/ < dconf-bmw.ini

Customization

Terminal

If you are using Kitty, set your preferred terminal as default terminal by editing your /etc/bash.bashrc file and changing the line xterm*|rxvt*|Eterm|aterm|kterm|gnome* to add kitty*, so we should have xterm*|rxvt*|Eterm|kitty*|aterm|kterm|gnome*.

Shell

Currently Athena uses mainly FISH shell but in the future users can choose among their favorite shells.

Setting immediately FISH as default shell is not good, because, since FISH and BASH have different parsing rules, if our default shell is FISH and we start a user session, /etc/profile, /etc/profile.d/*.sh and .bashrc scripts cannot be sourced because falling in error since they are "sourced" by FISH rules and not BASH rules.

A solution is to have the user associated to BASH in /etc/passwd and then calling FISH in .bashrc file by adding at the end:

if [[ $(ps --no-header --pid=$PPID --format=comm) != "fish" && -z ${BASH_EXECUTION_STRING} ]]
then
        exec fish
fi

The equivalent of FISH for .bashrc is ~/.config/fish/config.fish. Edit the file ~/.config/fish/config.fish, creating it if it does not exist.

Fonts

The main used font is JetBrains: https://www.jetbrains.com/lp/mono/#how-to-install

ISO Approach

ISO approach consists of the creation of an ISO file we can use for installing the OS in a flexible manner. This method is based on the usage of Archiso. Please, read its documentation on how to set it. Refer to https://wiki.archlinux.org/title/archiso#Kernel for speeding up the build process.

Structure

Here we suppose we already set Archiso and we proceed to build our ISO.

We make our project folder by mkdir -p ~/athena-iso and inside of it we put archiso content as explained in the official documentation. We should have a tree directory similar to this:

athena-iso
└── archlive
    ├── airootfs
    │   ├── etc
    │   │   ├── hostname
    │   │   ├── locale.conf
    │   │   ├── localtime -> /usr/share/zoneinfo/UTC
    │   │   ├── mkinitcpio.conf
    │   │   ├── mkinitcpio.d
    │   │   │   └── linux.preset
    │   │   ├── modprobe.d
    │   │   │   └── broadcom-wl.conf
    │   │   ├── motd
    │   │   ├── pacman.d
    │   │   │   └── hooks
    │   │   │       ├── 40-locale-gen.hook
    │   │   │       ├── uncomment-mirrors.hook
    │   │   │       └── zzzz99-remove-custom-hooks-from-airootfs.hook
    │   │   ├── passwd
    │   │   ├── resolv.conf -> /run/systemd/resolve/stub-resolv.conf
    │   │   ├── shadow
    │   │   ├── ssh
    │   │   │   └── sshd_config
    │   │   ├── systemd
    │   │   │   ├── journald.conf.d
    │   │   │   │   └── volatile-storage.conf
    │   │   │   ├── logind.conf.d
    │   │   │   │   └── do-not-suspend.conf
    │   │   │   ├── network
    │   │   │   │   ├── 20-ethernet.network
    │   │   │   │   ├── 20-wlan.network
    │   │   │   │   └── 20-wwan.network
    │   │   │   ├── system
    │   │   │   │   ├── choose-mirror.service
    │   │   │   │   ├── cloud-init.target.wants
    │   │   │   │   │   ├── cloud-config.service -> /usr/lib/systemd/system/cloud-config.service
    │   │   │   │   │   ├── cloud-final.service -> /usr/lib/systemd/system/cloud-final.service
    │   │   │   │   │   ├── cloud-init-local.service -> /usr/lib/systemd/system/cloud-init-local.service
    │   │   │   │   │   └── cloud-init.service -> /usr/lib/systemd/system/cloud-init.service
    │   │   │   │   ├── dbus-org.freedesktop.ModemManager1.service -> /usr/lib/systemd/system/ModemManager.service
    │   │   │   │   ├── dbus-org.freedesktop.network1.service -> /usr/lib/systemd/system/systemd-networkd.service
    │   │   │   │   ├── dbus-org.freedesktop.resolve1.service -> /usr/lib/systemd/system/systemd-resolved.service
    │   │   │   │   ├── etc-pacman.d-gnupg.mount
    │   │   │   │   ├── [email protected]
    │   │   │   │   │   └── autologin.conf
    │   │   │   │   ├── livecd-alsa-unmuter.service
    │   │   │   │   ├── livecd-talk.service
    │   │   │   │   ├── multi-user.target.wants
    │   │   │   │   │   ├── choose-mirror.service -> ../choose-mirror.service
    │   │   │   │   │   ├── hv_fcopy_daemon.service -> /usr/lib/systemd/system/hv_fcopy_daemon.service
    │   │   │   │   │   ├── hv_kvp_daemon.service -> /usr/lib/systemd/system/hv_kvp_daemon.service
    │   │   │   │   │   ├── hv_vss_daemon.service -> /usr/lib/systemd/system/hv_vss_daemon.service
    │   │   │   │   │   ├── iwd.service -> /usr/lib/systemd/system/iwd.service
    │   │   │   │   │   ├── livecd-talk.service -> /etc/systemd/system/livecd-talk.service
    │   │   │   │   │   ├── ModemManager.service -> /usr/lib/systemd/system/ModemManager.service
    │   │   │   │   │   ├── pacman-init.service -> ../pacman-init.service
    │   │   │   │   │   ├── qemu-guest-agent.service -> /usr/lib/systemd/system/qemu-guest-agent.service
    │   │   │   │   │   ├── reflector.service -> /usr/lib/systemd/system/reflector.service
    │   │   │   │   │   ├── sshd.service -> /usr/lib/systemd/system/sshd.service
    │   │   │   │   │   ├── systemd-networkd.service -> /usr/lib/systemd/system/systemd-networkd.service
    │   │   │   │   │   ├── systemd-resolved.service -> /usr/lib/systemd/system/systemd-resolved.service
    │   │   │   │   │   ├── vboxservice.service -> /usr/lib/systemd/system/vboxservice.service
    │   │   │   │   │   ├── vmtoolsd.service -> /usr/lib/systemd/system/vmtoolsd.service
    │   │   │   │   │   └── vmware-vmblock-fuse.service -> /usr/lib/systemd/system/vmware-vmblock-fuse.service
    │   │   │   │   ├── network-online.target.wants
    │   │   │   │   │   └── systemd-networkd-wait-online.service -> /usr/lib/systemd/system/systemd-networkd-wait-online.service
    │   │   │   │   ├── pacman-init.service
    │   │   │   │   ├── reflector.service.d
    │   │   │   │   │   └── archiso.conf
    │   │   │   │   ├── sockets.target.wants
    │   │   │   │   │   └── systemd-networkd.socket -> /usr/lib/systemd/system/systemd-networkd.socket
    │   │   │   │   ├── sound.target.wants
    │   │   │   │   │   └── livecd-alsa-unmuter.service -> ../livecd-alsa-unmuter.service
    │   │   │   │   └── systemd-networkd-wait-online.service.d
    │   │   │   │       └── wait-for-only-one-interface.conf
    │   │   │   └── system-generators
    │   │   │       └── systemd-gpt-auto-generator -> /dev/null
    │   │   └── xdg
    │   │       └── reflector
    │   │           └── reflector.conf
    │   ├── root
    │   └── usr
    │       └── local
    │           ├── bin
    │           │   ├── choose-mirror
    │           │   ├── Installation_guide
    │           │   └── livecd-sound
    │           └── share
    │               └── livecd-sound
    │                   └── asound.conf.in
    ├── bootstrap_packages.x86_64
    ├── efiboot
    │   └── loader
    │       ├── entries
    │       │   ├── 01-archiso-x86_64-linux.conf
    │       │   ├── 02-archiso-x86_64-speech-linux.conf
    │       │   ├── 03-archiso-x86_64-ram-linux.conf
    │       │   └── 04-archiso-x86_64-ram-speech-linux.conf
    │       └── loader.conf
    ├── grub
    │   └── grub.cfg
    ├── packages.x86_64
    ├── pacman.conf
    ├── profiledef.sh
    └── syslinux
        ├── archiso_head.cfg
        ├── archiso_pxe.cfg
        ├── archiso_pxe-linux.cfg
        ├── archiso_sys.cfg
        ├── archiso_sys-linux.cfg
        ├── archiso_tail.cfg
        ├── splash.png
        └── syslinux.cfg

The airootfs directory is used as the starting point for the root directory (/) of the live system on the image. All its contents will be copied over to the working directory before packages are installed.

Place any custom files and/or directories in the desired location under airootfs/. For example, if the user has a set of iptables scripts on its current system to be used on its live image, copy them over as such:

$ cp -r /etc/iptables archlive/airootfs/etc

Similarly, some care is required for special configuration files that reside somewhere down the hierarchy. Missing parts of the directory structure can be simply created with mkdir.

To add a file to the root's home directory, place it in archlive/airootfs/root/. To add a file to all other users' home directories, place it in archlive/airootfs/etc/skel/.

Note: Custom files that conflict with those ones provided by packages will be overwritten or generate an error unless a package specifies them as backup files in its PKGBUILD file.

By default, permissions will be set as 644 for files and 755 for directories. All of them will be owned by the root user. To set different permissions or ownership for specific files and/or folders, use the file_permissions associative array in profiledef.sh.

Custom files can be added inside the ISO in two ways: by making a remote or local repository or putting them directly inside the airootfs tree.

Summarizing, we can modify directly the files inside airootfs directory that corresponds to our future system root, and we can add packages we would like to install on our Athena OS by specifying them inside athena-iso/archlive/packages.x86_64 file. Furthermore, if some of our files in airootfs need particular permissions, we can add them inside athena-iso/archlive/profiledef.sh file.

Services

About systemd services, for enabling them after the installation, you can already insert them in the ISO in this way:

sudo rm -rf $ARCHLIVE/airootfs/etc/systemd/system/multi-user.target.wants/NetworkManager.service
sudo rm -rf $ARCHLIVE/airootfs/etc/systemd/system/systemd-timesyncd.service
sudo rm -rf $ARCHLIVE/airootfs/etc/systemd/system/display-manager.service
sudo ln -s /usr/lib/systemd/system/NetworkManager.service $ARCHLIVE/airootfs/etc/systemd/system/multi-user.target.wants/NetworkManager.service
sudo ln -s /usr/lib/systemd/system/systemd-timesyncd.service $ARCHLIVE/airootfs/etc/systemd/system/systemd-timesyncd.service
sudo ln -s /usr/lib/systemd/system/gdm.service $ARCHLIVE/airootfs/etc/systemd/system/display-manager.service
sudo ln -s /usr/lib/systemd/system/multi-user.target.wants/vboxservice.service $ARCHLIVE/airootfs/etc/systemd/system/vboxservice.service
sudo ln -s /usr/lib/systemd/system/multi-user.target.wants/vmtoolsd.service $ARCHLIVE/airootfs/etc/systemd/system/vmtoolsd.service
sudo ln -s /usr/lib/systemd/system/multi-user.target.wants/vmware-vmblock-fuse.service $ARCHLIVE/airootfs/etc/systemd/system/vmware-vmblock-fuse.service

Users and Groups

Another important topic is related to Users and Groups as specified in https://wiki.archlinux.org/title/archiso#Installation Note that in the guide, in etc/passwd, /bin/zsh is used as shell, and if ZSH is not installed because the user wants to use another shell, for example BASH, change it to /bin/bash or other shells will be installed in the ISO, otherwise the user will get authentication failure. etc/passwd must be like:

root:x:0:0:root:/root:/bin/bash
liveuser:x:1000:1000::/home/liveuser:/usr/bin/bash

On the ISO, if we are using GDM as Display Manager, for autologin it is important to create /path/to/airootfs/etc/gdm/custom.conf file with the following content:

[daemon]
AutomaticLoginEnable=True
AutomaticLogin=liveuser

where liveuser is our Live User account. For removing automatically that line after the installation of the target system (because the liveuser won't exist anymore), the command sed -i '/^AutomaticLogin=liveuser/d' /etc/gdm/custom.conf has been implemented inside of shellprocess-before.conf that will be executed by Calamares Installer.

Furthermore, there will be aspects the user wants to be run at the first login after the system installation. For example, since dconf command cannot be run in chrooted environment, the user can create a run-once.sh script placed in airootfs/etc/profile.d folder. Then, give root executable permission on archiso/profiledef.sh file.

ISO Labeling

In archiso/profiledef.sh file important variables are defined, as iso_label that defines the %ARCHISO_LABEL% used in archiso/grub/grub.cfg (UEFI mode) and archiso/syslinux/archiso_pxe-linux.cfg and archiso/syslinux/archiso_sys-linux.cfg files (BIOS/Legacy mode). In Athena the iso_label is always defined by iso_label="ATHENA-OS" string (Linux environment is case-sensitive) in order to be compliant with both ISO-9660 and FAT32 requirements (label shorter than 11 characters). Being compliant with FAT32 requirements will allow USB burning software as Rufus to work properly when burning the ISO and boot Athena by USB on the computer. Remember that RUFUS will convert labels as uppercase, for this reason our iso_label value should be an uppercase string. If we use a label longer than 11 characters, it could happen that the label assigned by Rufus (or similar tools), that will be cut in 11 characters, won't match the label defined in %ARCHISO_LABEL% variable, so the GRUB won't be able to find the USB device and will return an error like "error: no device found".

First Boot

The content of run-once.sh checks two flag files in the home folder of the current user: one for running offline commands at first boot; the other one for running online commands at first boot. If these flag files exist, then execute the customized commands and delete these flag files. In this way, the user executes these commands only at the first boot of a user. In case the network connection is not available at the first boot, the network flag file is kept until the session is logged in by keeping an Internet connection.

Setting Repositories

Remember to set always the repositories you need in the host environment, as BlackArch and Chaotic repositories. In this way, you can directly install keyrings and mirrorlist on the ISO by inserting in package.x86_64 the following packages: blackarch-keyring, blackarch-mirrorlist, chaotic-keyring and chaotic-mirrorlist.

Creating ISO

When we finished to set everything, we can type mkarchiso ~/athena-iso/archlive for making our ISO. This process will create the work directory that contains all the temporary files needed for making our ISO, and out directory that will contain our final ISO file.

At the end of the ISO creation, execute sudo pacman -Scc in order to clean /var/cache/pkg folder. Do the same process also on the target machine when the installation of the operating system ends.

All the third-party tools and environment customization manually implemented in the Direct Approach are automatically implemented in the ISO Approach.

The content of airootfs and the other files mentioned here are stored in https://github.com/Athena-OS/athena-iso repository, so feel free to give a look on how they are built.