SkillAgentSearch skills...

Rustysd

A service manager that is able to run "traditional" systemd services, written in rust

Install / Use

/learn @KillingSpark/Rustysd
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

rustysd

Rustysd is a service manager that tries to replicate systemd behaviour for a subset of the configuration possibilities. It focuses on the core functionality of a service manager, not requiring to be PID1 (aka init process).

Will this replace systemd?

TLDR: No, rustysd is no dedicated replacement. It is an opportunity for the niches where systemd could not get it's foot down to profit (more easily) from the ecosystem around systemd.

Very likely no. There are a lot of reasons, but most importantly: it works and provides features that rustysd will likely never provide.

This project might be whats needed to show that the core systemd functionality is not very hard to replicate and that all the advantages of having a systemd-like service manager can be brought to many other platforms is very much feasible without having to port all of systemd. There are some (a lot?) platforms that rust does not (yet) fully support so the maintainers will understandably reject using rustysd as their main service manager. But having rustysd as an example might help other efforts in more portable languages.

Rustysd also opens up usage of systemd services outside of systemd based linux distros (like alpine linux, commonly used in docker containers and small vms) and freebsd.

General info

For now this project is just out of interest how far I could come with this and what would be needed to get a somewhat working system. It is very much a proof of concept / work in progress. For the love of god do not use this in anything that is important.

It does look somewhat promising, most needed features are there. There are a lot of tests missing and more care needs to be taken so that rustysd itself never panics.

Short intro to systemd / rustysd

Systemd/rustysd operate on so called "units". These are smallish separate entities in the system like a single service. These units can be handled independently but can specify relations to other units that they "need". The unit of service-abc can say "I need the unit of service-xyz to be started before I do".

The second thing systemd/rustysd bring to the table is socket activation. Services that specify sockets do not need to be started immediately, but rather when there is activity on their socket(s). This enables faster startup times by starting services lazily when they are needed.

Additionally systemd provices a lot more unit-types besides services and sockets which rustysd does not (and for most will likely never) support.

Scope of rustysd

What is explicitly in scope of this project

  1. Startup sorted by dependencies (parallel if possible for unrelated units)
  2. Startup synchronization via *.target units
  3. Socket activation of services

What is explicitly out of scope (for now, this project is still very young):

  1. Timers (Cron should do fine for 99% of usecases)
  2. Mounts (It is actually useful to have these as units but I don't think the gains outweigh the added complexity)
  3. Device (Same argument as for Mount)
  4. Path activation (Might get included.)
  5. Scopes (Nope. If you start processes outside of rustysd you need to manage them yourself. Maybe a second instance of rustysd? ;))
  6. Slices (this might be added as it is fairly important if you are not running inside of a container)

Gitter

About slices

I dont think it is viable for a cross-platform project to support slices. In general I think it would be more sensible to put that responsibility on other tools.

I imagine something along the lines of dockers 'runc' but not specialized to the container environment. Let's call the imaginary tool 'restrict', the usage I imagine would be along the lines of:

restrict -cmd "/my/binary arg1 arg2 arg3" -mem_max=5G -io_max=10G/s

This would setup the process with the given restrictions and then exec into the given cmd. with this kind of tool there are a few benefits:

  1. Rustysd doesnt have to concern itself with how a platform does resource restriction, but there can be separate tools for each platform (if possible)
  2. Clear separation of concerns. Rustysd manages service lifetimes. It does not (or only for relatively trivial stuff) manage the runtime environment for those services.
  3. The tool can be useful to other contexts aswell

For linux there are some existing utilities from the docker/container/oci-spec space:

  • The oci implementation from the docker guys runc
  • For people who want completely static builds, an alternative oci implementation crun seems great
  • It SHOULD be possible to do something similar for BSD jails

Goals

Since this project is very young and wasn't started with any particular goal in mind, I am open to any and all ideas. Here are some that have been brought up that seem sensible. None of this is definitive though.

  1. Provide a PID1 for containers/jails, so unaltered systemd depending services can be run in a container/jail
  2. Provide full init capabilities so this can be used for OS's like redox os or debian/kFreeBSD
  3. Be platform agnostic as long as it's unix (I develop on linux but I'll try to test it on FreeBSD when I add new platform specific stuff)

About platform independence

Here is a list of features rustysd currently assumes the platform to provide. The list also contains suggestions about which features could be cut and which consequence that would have (e.g. see the filedescriptor point). Everything here is written in unix terms but it should not be too much work to write a compatibility shim if an equivalents exist on the target platform. It's not too many features that must exist for the port to work in a usable way (and they are mostly basic OS functionality anyways).

  1. forking
  2. getting the current process id
  3. file descriptors that can be passed to child processes when forking
    • Maybe we dont have to have this. We could just make sockets and socket-activation an optional feature for unixy platforms
    • Then forking would be optional too, just having the ability to launch new executables in a new process would suffice
  4. (Un-)Mark file descriptors for closing on exec()'ing if forking with passed fds is supported
  5. Select()'ing on filedescriptors (not just for socket activation but for listening on stdout/err of child processes)
  6. Creating a pipe/eventfd/... for interrupting the selects (also a way to activate/reset those, write(/read() for pipes for example)
  7. dup2()'ing filedescriptors for providing fds at fd index 3,4,5,...
  8. Creating process-groups
  9. signals from the platform when a child exits / gets terminated in any way
  10. waitpid()'ing for the exited children
  11. sending (kill/terminating) signals to whole process groups (as long as we care about cleanup after killing, maybe the platform handles this in another smart way?)
  12. setting env variables (currently handled with libc because the rust std contains locks which currently break on forking)
  13. setting the current process as a subprocess reaper (might not be that important, other platforms might handle reparenting of orphaned processes differently than unix)
  14. changing the user id to drop privileges
  15. an implementation of getpwnam_r and getgrnam_r. These can be swapped for getpwnam/getgrnam if needed
    • They could also be ignored, restricting the values of User, Group, and SupplementaryGroups to numerical values

About platform dependent stuff

There are some parts that are platform dependent. Those are all optional and behind feature flags.

Cgroups

Rustysd can employ cgroups for better control over which processes belong to which service. Resource-limiting is still out of scope for rustysd. Cgroups are only used to make the features rustysd provides anyways more reliable.

On other systems there might arise issues if a service forks of processes which move into another process-group. If these are not cleanly killed by the stop/posstop commands they will be orphaned and survive. This is (if I understand correctly) the way other service manager handle this too.

Why no jails

One possibility would be to use BSD jails but that seems somewhat hacky since rustysd would have to chain-load the actual service command with a jail command. Rustysd could check if a service is started in a jail anyways and then kill that jail. But that could lead to other problems if the jail is meant to be long lived. In short I see no clean way to employ jails for process-management

What works

This section should be somewhat up to date with what parts are (partly?) implemented and (partly?) tested. If you find anything does actually not work please file me an issue!

For an in-depth comparison of systemd and rustysd see the feature-comparison.md file. It is generated by the tools/gen_feature_comparison.py (shoutout to wmanley who wrote the initial script!). It currently is somewhat pessimistic, I will work on improving the comparison of the features rustysd actually does support (see below for a list of supported features).

General features

Of rustysd itself

  • Parsing of service files (a subset of the settings are recognized)
  • Parsing of socket files (a subset of the settings are recognized)
  • Ordering of services according to the before/after relations
  • Killing services that require services that have died
  • Matching services and sockets either by name or dynamically by parsing the appropiate settings in the .service/.socket files
  • Passing filedescriptors to the daemons as systemd clients expect them (names and all that good stuff)
  • Pretty much all parts of the sd_notify API
  • Waiting for the READY=1 notification for services of type notify
  • Waiting for services of type dbus
  • Waiting for multiple dependencies
  • Target units to synchronize the startup
  • Send

Related Skills

View on GitHub
GitHub Stars573
CategoryDevelopment
Updated5d ago
Forks19

Languages

Rust

Security Score

100/100

Audited on Mar 26, 2026

No findings