mirror of https://github.com/nocturn9x/nimd.git
125 lines
8.7 KiB
Markdown
125 lines
8.7 KiB
Markdown
# Nim Daemon - An init system written in Nim
|
|
A minimal, self-contained, dependency-based Linux init system written in Nim.
|
|
|
|
## Note
|
|
|
|
The code in here is pretty bad: in fact, it's horrible (and it _barely_ works). I'm just messing around to get the basics done, sorry for that,
|
|
but I need to get a proof of concept done before starting to do some actually sensible programming.
|
|
|
|
I mainly made this thing for fun and as an excuse to learn more about the mysterious PID 1 and the Linux kernel in general. If you are like me
|
|
and love getting your hands dirty, I truly recommend trying an endeavor like this as I haven't had this fun cobbling something together in a very
|
|
long time. Sometimes programming only large scale software is boring, go figure.
|
|
|
|
## Disclaimers & Functionality
|
|
|
|
This software is developed on a _"It works on my (virtual) machine"_ basis. I don't perform any extensive testing: if this thing unmounts your
|
|
root partition while you're saving your precious family photos, I can't do much (although it probably won't). I currently test NimD inside a
|
|
minimal Alpine VM that runs the 5.10.0-9-amd64 version of the Linux kernel.
|
|
|
|
NimD is developed **for Linux only**, as that's the kernel my OS uses: other kernels are not supported at all and NimD **will** explode with fancy
|
|
fireworks if you try to run it unmodified on other kernels (although probably things like BSD and Solaris are not that hard to add support for).
|
|
|
|
NimD is not particularly secure (although basic checks like making sure regular users can't reboot the machine are in place), but it doesn't need to be: the only thing it does is run the services you provide to it, that's it*. No `nimd-modulenamed` madness, no `libnimd.so` libraries to link against, NimD only runs your services: if it blows up, it's your fault (or it's a bug).
|
|
|
|
NimD expects the 3 [standard streams](https://en.wikipedia.org/wiki/Standard_streams) to be properly connected to `/dev/console` (which is something all modern versions of the Linux kernel do). I tried connecting them manually, but I was out of luck: if you happen to know how to check for (or open) them and connect them manually, please make a PR, I'd love to hear how to do that.
|
|
|
|
_*_: Well, almost. If you don't wanna write oneshot services for simple things like creating symlinks/directories (especially if you plan running BSD ports of
|
|
some program on Linux) and mounting your drives then NimD can do it for you, but just because it _can_ doesn't mean it _has to_: you choose! NimD has a builtin
|
|
fstab parser and can operate entirely independently of the `mount` command, since it directly hooks up to `mount`, `umount` and `umount2` inside `sys/mount.h`
|
|
|
|
## Setup
|
|
|
|
NimD expects to be installed like so:
|
|
- `/etc/nimd` -> Contains configuration files and utilities like `reboot` and `poweroff`
|
|
- `/etc/nimd/runlevels` -> Contains the runlevels (`boot`, `default`, `shutdown`)
|
|
- `/etc/nimd/nimd.conf` -> NimD's own configuration file
|
|
- `/var/run/nimd.sock` -> Unix domain socket for IPC
|
|
- `/etc/runlevels` -> Symlink to `/etc/nimd/runlevels`
|
|
- `/sbin/nimd` -> Actual NimD executable
|
|
- `/sbin/init` -> Symlink to `/sbin/nimd`
|
|
- `/bin/{poweroff,shutdown,reboot,halt}` -> Minimal utilities that communicate with NimD to poweroff/shutdown/reboot/halt the machine
|
|
- `/bin/nimdctl` -> Utility to interact with NimD (add/remove/start/stop services, read logs, inspect services' status, etc)
|
|
|
|
|
|
__Note__: The runlevels directory contains `*.conf` files (or they can also be symlinks, NimD doesn't care): those are NimD's own unit files.
|
|
|
|
|
|
## Unit files
|
|
|
|
Services in NimD are called _unit files_ or just _units_ (I know, __very__ original). They are configuration files that tell NimD what to do once
|
|
it has booted your system
|
|
|
|
### Dependency management
|
|
|
|
Unlike some other init systems (most notably, runit) NimD is _dependency based_: to understand this relatively simple concept disguised as a fancy term,
|
|
you have to understand NimD (like many others) relies on the concepts of _dependents_ (units that _depend_ on some others to work) and _providers_ (units
|
|
that _provide_ services to their dependents and that may in turn have dependencies themselves). For example, if you wanna start an SSH server, you probably
|
|
want to make sure your disks are mounted and that your network has been set up. To do that, you can write something like this:
|
|
|
|
```
|
|
[Service]
|
|
name = ssh # The name of the service
|
|
description = Secure Shell Server # A short description
|
|
type = simple # Other option: oneshot (i.e. runs only once, implies supervised=false)
|
|
exec = /usr/bin/sshd <args> # Note: this is not passed trough the shell, it's executed directly
|
|
depends = net,fs # This service will be started only when these dependencies are satisfied
|
|
provides = ssh # Dependents can also be providers
|
|
restart = always # Other options are: never, onFailure
|
|
restartDelay = 10 # NimD will wait this many seconds before trying to start it again
|
|
supervised = true # This is the default. Disable it if you don't need NimD to watch for it
|
|
workDir = /usr/bin # The service's working directory
|
|
|
|
[Logging]
|
|
stderr = /var/log/sshd # Path of the stderr log for the service
|
|
stdout = /var/log/sshd # Path of the stdout log for the service
|
|
stdin = /dev/null # Path of the stdin fd for the service
|
|
```
|
|
|
|
__Note__: Unsupervised services cannot be restarted, as NimD has no control over them once they're spawned.
|
|
|
|
|
|
A dependency name can either be the name of a unit file (case sensitive, but without the `.conf` extension), or one of the following placeholders:
|
|
- `net` -> Stands for network connection. Services like NetworkManager and dhcpcd should be set as providers for this
|
|
- `fs` -> If you mount your disks using a oneshot service (recommended for the best experience), your service should provide this
|
|
- `ssh` -> The service provides some sort of SSH functionality
|
|
- `ftp` -> The service provides an FTP server
|
|
- `http` -> The service is an HTTP webserver
|
|
|
|
Note that NimD resolves placeholders before service names: this means that if you have a service named `ssh.conf`, using `ssh` as
|
|
a dependency will __not__ set that service as a dependency and will __not__ override the default behavior unless said unit file also has
|
|
`provides=ssh` in it. Also note that multiple providers for the same service raise a warning by default and cause NimD to let the alphabet decide
|
|
which dependency is started (i.e. they are sorted lexicographically by their filename, without the extension, and the first is picked), but this
|
|
behavior can be changed (e.g. raising an error instead)
|
|
|
|
## Configuring NimD
|
|
|
|
NimD's own configuration file is located at `/etc/nimd/nimd.conf` and its syntax is similar to those of unit files (i.e. uses an
|
|
INI-like structure), but the options are obviously different. An example config would look something like this:
|
|
|
|
```
|
|
[Logging]
|
|
level = info # Levels are: trace, debug, info, warning, error, critical, fatal
|
|
logFile = /var/log/nimd # Path to log file
|
|
|
|
[Filesystem]
|
|
autoMount = true # Automatically parses /etc/fstab and mounts disks
|
|
autoUnmount = true # Automatically parses /proc/mounts and unmounts everything on shutdown
|
|
fstabPath = /etc/fstab # Path to your system's fstab (defaults to /etc/fstab)
|
|
createDirs = "/path/to/dir:perms," # Creates these directories with the given permissions on boot. Empty to disable
|
|
createSymlinks = "/path/to/symlink:/path/to/dest," # Creates these symlinks on boot. Empty to disable
|
|
|
|
[Misc]
|
|
controlSocket = /var/run/nimd.sock # Path to the Unix domain socket to create for IPC
|
|
setHostname = true # Set to true to set the machine's hostname automatically from /etc/hostname
|
|
onDependencyConflict = skip # Other option: warn, error
|
|
workers = 1 # Number of worker processes to use when spawning services
|
|
restartDelay = 10 # Delay (seconds) that nimd will wait before restarting itself after crashes
|
|
sigtermDelay = 90 # Delay (seconds) that nimd will wait before terminating child processes with
|
|
# SIGKILL after sending a more gentle SIGTERM upon shutdown or exit
|
|
```
|
|
|
|
## Testing NimD
|
|
|
|
NimD is not quite ready for production yet, but in the `scripts` folder you can find a few simple bash scripts to test NimD
|
|
in a minimal Alpine Linux VM using QEMU. Note that due to weirdness in how stdout is handled on the VGA port, the VM will use
|
|
the serial port (ttyS0) as output by default (you can change this in the kernel parameters) |