Thursday, January 4, 2018

A Quick Explanation of Project Panux

A while back I decided that I wanted to make my own Linux distro because I disliked many design choices. Different distros have gone in different directions and made decisions that benefited a specific use. I wanted to make a distro that would work well for everything - with a small footprint so it could be used on embedded devices/routers, parallel init so that desktops would load up fast, and a large amount of control over packages while making it easy for non-tech-savvy users to install packages. I originally tried to base it off of Debian, then Arch, and then I gave up. Eventually, I decided that there was nothing for me to base it off of, and it was time for me to make my own distro.

Package Generator
This was the most annoying part. I tried makefiles and shell scripts, and then gave up on panux. Several years later, having learned about Go and Docker, I wrote a system that translated yaml into makefiles using Go text/template to allow shortcuts and programmatic generation of commands (best use of this is in the Mesa packaging script). These makefiles are run in a docker container. Later I went back and optimized it, running the source-fetching makefile on the host so that the sources would be cached.
Package Manager (lpkg)
The package manager for panux has been completely rewritten at least 5 times (I lost track). Originally I wrote it in shell (shpkg), but then I ran into some issues due to limitations in Busybox shell. Later it was rewritten in Lua (hence the name lpkg). The Lua version was rewritten several times to add new features and make major changes to the package management system. Finally, I rewrote lpkg in shell again because lua was taking up too much space.

So now the package management system is written in shell, and uses minisign to verify package signatures. It only depends on minisign (which uses libsodium) and busybox - which allowed me to cut the base system down to 1 MB.
Coreutils & LibC
I originally planned to use uClibc for Panux, but realized it lacked important features and switched to musl. I use busybox for the core utilities (think sh, grep, echo, etc.). These are small - which is why I chose them.
Init System (linit)
Linit is currently the biggest difference between Panux and other Linux distros. LinitD is a daemon that runs init scripts. It uses epoll in order to deal with communication, instead of threads. The process of starting a process in linit is as follows:

  1. LinitD fork/execs a shell script called linit-start
  2. linit-start runs the corresponding init file in /etc/init.d with the start option
  3. Before running the start shell function in the init file, the depends shell function is run
  4. The depends shell function calls a shell function called dep which invokes linitctl start
  5. linitctl start connects to linitd over a unix socket and requests that the dependencies be started
  6. linitctl start waits for linitd to notify of completion
  7. The depends function completes and the start function is run
  8. The init script finishes and linit-start notifies LinitD of completion
  9. LinitD forwards the notification. . .
When it starts, LinitD starts an init script called boot which pulls everything in /etc/rc.d as a dependency - which starts the system. As you can see, LinitD does not actually have to do very much - almost everything is done by the init scripts. Because of this, LinitD only needs ~40KB of memory to run (for comparison my SysD is using 2.2 MB of normal mem and 3.2 MB of shared memory right now).