Page MenuHomeFreeBSD

rcd(8): new service manager daemon
Needs ReviewPublic

Authored by bapt on May 5 2026, 3:37 PM.
Tags
None
Referenced Files
F160322128: D56835.diff
Tue, Jun 23, 6:32 AM
F160318122: D56835.diff
Tue, Jun 23, 5:30 AM
F160294234: D56835.id179040.diff
Tue, Jun 23, 12:27 AM
F160284723: D56835.diff
Mon, Jun 22, 10:41 PM
F160284627: D56835.diff
Mon, Jun 22, 10:39 PM
F160279090: D56835.id179907.diff
Mon, Jun 22, 9:09 PM
Unknown Object (File)
Mon, Jun 22, 2:30 PM
Unknown Object (File)
Mon, Jun 22, 1:02 PM

Details

Summary

rcd is called by init(8), it reads service definitions from UCL unit files,
builds a dependency graph, and starts services in parallel.
After boot completes, it remains running as a supervisor daemon, automatically
restarting failed services and accepting control commands via a UNIX domain
socket.

Design goals:

  • Fast parallel boot via dependency DAG
  • No PID races: use posix_spawn with process descriptors
  • No process escape: become subreaper via procctl(2)
  • Socket activation: pre-bind sockets, pass via fd inheritance
  • Resource control: per-service limits via rctl(2)
  • Service isolation: native jail(2) integration
  • OOM protection: procctl(2) PROC_SPROTECT
  • UCL-based unit files via libucl
  • Embedded Lua interpreter for inline service hooks
  • Template units for per-instance services
  • Per-service access control
  • JSON Schema validation of unit files
  • Full backward compatibility with existing rc.d scripts
  • Called by init(8) with no changes to init

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 73902
Build 70785: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
sbin/rcd/xmalloc.h
2

This feels like we could leverage a lot of the constructs provided by NetBSD (or was it OpenBSD?) in this space to avoid reinventing the wheel. I'd need to find the exact source file, but I've seen similar constructs in those spaces -- memory serves me correctly, it was NetBSD...

sbin/rcd/xmalloc.h
2

I've had a quick chat with bapt (here? irc?) whre I'd like a bunch of this stuff broken out into libraries for future reuse.

ngie requested changes to this revision.Tue, Jun 16, 5:25 PM

Please add [explicit? implicit?] versioning information to the schema and UCL files -- otherwise you risk problems when upgrading/downgrading schema/unit files.

sbin/rcd/schema/unit.schema.json
147

If this is an opaque object, does it make sense to add a separate property to explicitly state the type? That would allow other end-users the ability to extend rcd as needed with other language bindings in the future (golang, python, rust, ...).

This revision now requires changes to proceed.Tue, Jun 16, 5:25 PM
sbin/rcd/xmalloc.h
2

xmalloc, xstring, vec.h and hash.h has been exercise for years in pkg, why would I replace it with openbsd's or netbsd's?

This is a lot of change to digest in a single differential. I don't want to recommend "using LLMs" for reviewing -- it's a slippery slope for all involved as LLMs can be confident bullshitters/idiots.

Is there any way this work could please be broken down into a diff stack?

sbin/rcctl/rcctl.c
262

*sigh* this code (for example) would be a lot simpler/cleaner with Boost::Program_options / C++ strings :(.

sbin/rcd/compat.c
43–45

Why isn't this using regular expressions or a more formalized lexer/parser like yacc, etc?

sbin/rcd/depgraph.c
71

Please add sys/queue.h for these macros.

sbin/rcd/hash.c
8–11

https://xkcd.com/927/ comes to mind if this came from libpkg.

sbin/rcd/hash.h
2

Or maybe just use C++'s std::map / std::unsorted_map data structures instead? I would be shocked if one of the other BSDs hasn't already invented this wheel before (which we might be able to borrow/contribute bits back for).

sbin/rcd/jail_svc.c
33–38
  • Should these be managed in config files?
  • What about old versions of jails/rcd vs newer hosts where there might be a feature/settings mismatch?
sbin/rcd/luaexec.c
2

This looks like it does a lot that lutok tries to do :(...

sbin/rcd/mum.h
2

Interesting copyright notice.. how're we going to stay on top of changes to this header if licensing changes, etc?

Please add [explicit? implicit?] versioning information to the schema and UCL files -- otherwise you risk problems when upgrading/downgrading schema/unit files.

for versionning I was thinking like in pkg to never break backward compatibility, by always adding new keys and never mutate existing one, this also solves forward compatibility because rcd will not validate a unit made in the new format.

ziaee requested changes to this revision.Tue, Jun 16, 6:25 PM

Hey Bapt! This is super cool. I've been busy with 15.1 docs and not had time to review this. Here is the SBOM style review, I will hopefully follow up with the docs review this week.

sbin/rcctl/rcctl.8
2

According to our style guides for consistency with C comments.

sbin/rcctl/rcctl.c
2–6

Copyright comes first in SPDX only header, no hypen.

sbin/rcd/compat.c
2–6

Ditto

sbin/rcd/control.c
2–6

Ditto

sbin/rcd/depgraph.c
2–6

Ditto

sbin/rcd/enable.c
2–6

Ditto

sbin/rcd/hash.c
2–6

Ditto

sbin/rcd/hash.h
2–6

Ditto

sbin/rcd/jail_svc.c
2–6

Ditto

sbin/rcd/log.c
2–6

Ditto

sbin/rcd/luaexec.c
2–6

Ditto

sbin/rcd/mum.h
2

Please add the SPDX-License-Identifier: MIT to the top of this.

sbin/rcd/process.c
2–6

Copyright comes first if using SPDX only license header. Hyphen is useless and no longer recommended.

sbin/rcd/rcd-exec.8
2

Please prepend a blank comment for consistency with the style guides and C style comments.

sbin/rcd/rcd-exec.c
2–6

Copyright comes first in SPDX only license header. Useless hyphen is no longer recommended.

sbin/rcd/rcd-lua.3
2

Please prepend a blank comment for consistency with style guides and C style comments.

sbin/rcd/rcd.8
2

Please prepend a blank comment for consistency with style guides and C style comments.

sbin/rcd/rcd.c
2–7

Copyright comes first in SPDX only license header. Please remove useless hyphen for consistency with style guides. Also, you had an extra blank comment line.

sbin/rcd/rcd.conf.5
2

Please prepend a blank comment line for consistency with style guides and C style comments.

sbin/rcd/rcd.d.5
2

Please prepend a blank comment line for consistency with style guides and C style comments.

sbin/rcd/rcd.h
2–27

Why use full license? you didn't do that in the others?

sbin/rcd/rctl_mgr.c
2–6

Copyright comes first in SPDX only license headers. Also, please remove useless hyphen for consistency with style guides.

sbin/rcd/sockact.c
2–6

Copyright comes first in SPDX only license headers. Also, please remove useless hyphen for consistency with style guides.

sbin/rcd/tests/compat_test.c
2–6

Ditto

sbin/rcd/tests/config_test.c
2–6

Ditto

sbin/rcd/tests/depgraph_test.c
2–6

Ditto

sbin/rcd/tests/enable_test.c
2–6

Ditto

sbin/rcd/tests/jail_svc_test.c
2–6

Ditto

sbin/rcd/tests/luaexec_test.c
2–7

Ditto

sbin/rcd/tests/process_test.c
2–6

Ditto

sbin/rcd/tests/rcd_jail_test.sh
2–5

Ditto

sbin/rcd/tests/rctl_test.c
2–6

Ditto

sbin/rcd/tests/sockact_test.c
2–6

Ditto

sbin/rcd/tests/unit_test.c
2–6

Ditto

sbin/rcd/unit.c
2–6

Ditto

sbin/rcd/vec.h
2

Please remove useless hyphen for consistency with style guides.

And, when invoked as /sbin/rcd on a live system, I'm getting:

1700  -  Is    0:00.02 |-- rcd -v
2599  -  Z     0:00.00 | |-- <defunct>
2600  -  Z     0:00.00 | |-- <defunct>
2601  -  Z     0:00.00 | |-- <defunct>
2602  -  Z     0:00.00 | |-- <defunct>
2603  -  Z     0:00.00 | |-- <defunct>
3180  -  Z     0:00.00 | |-- <defunct>
3182  -  Z     0:00.00 | |-- <defunct>
3185  -  Z     0:00.00 | |-- <defunct>
3228  -  I     0:00.00 | |-- rcd-exec: reaper: syslogd (pid 3378) (rcd-exec)
3371  -  SCs   0:00.00 | | |-- /usr/sbin/syslogd -s
3376  -  I     0:00.00 | | |-- syslogd: syslogd.casper (syslogd)
3378  -  Is    0:00.00 | | `-- syslogd: system.net (syslogd)
3359  -  Z     0:00.00 | |-- <defunct>
3361  -  Z     0:00.00 | |-- <defunct>
3362  -  Z     0:00.00 | |-- <defunct>
3364  -  Z     0:00.00 | |-- <defunct>
3365  -  Z     0:00.00 | |-- <defunct>
3368  -  Z     0:00.00 | |-- <defunct>
3536  -  I     0:00.00 | `-- rcd-exec: reaper: cron (pid 3715) (rcd-exec)
3715  -  Ss    0:00.00 |   `-- /usr/sbin/cron -s

too many defuct processes.

fixed

Still zombies:

1627  -  Z      0:00.00 | |-- <defunct>
1631  -  Z      0:00.00 | |-- <defunct>
1634  -  Z      0:00.00 | |-- <defunct>
1835  -  Z      0:00.00 | |-- <defunct>
1838  -  Z      0:00.00 | |-- <defunct>
1840  -  Z      0:00.00 | |-- <defunct>

otis@b14:~ % sudo procstat 1627
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1627    19    19    19     0   1 root     -         FreeBSD ELF64 logger
otis@b14:~ % sudo procstat 1631
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1631    19    19    19     0   1 root     -         FreeBSD ELF64 logger
otis@b14:~ % sudo procstat 1634
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1634    19  1634  1634     0   1 root     -         FreeBSD ELF64 logger
otis@b14:~ % sudo procstat 1835
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1835    19    19    19     0   1 root     -         FreeBSD ELF64 savecore
otis@b14:~ % sudo procstat 1838
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1838    19    19    19     0   1 root     -         FreeBSD ELF64 savecore
otis@b14:~ % sudo procstat 1840
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM
 1840    19    19    19     0   1 root     -         FreeBSD ELF64 savecore
In D56835#1311164, @imp wrote:

I'd like to echo the user requirement. I have not current need for it at $WORK, but can see a need in the future. Having the ability for users to tie into the startup, running as themselves, much like we allow users to add jobs to cron, at, etc would be a useful, but not gating, feature.

I believe that user units is actually an orthogonal feature of the system service manager. If we are to implement user units, IMO, it should be a cross-platform project that consumes pristine systemd unit descriptions and hooks into the OS via PAM. It doesn't even has to be a part of base, because only uses of user units I stumbled upon so far were coming from ports.

I'm working on a systemd-logind replacement, which will fill the user units hole nicely, but there is a ton of work to do.

In D56835#1311164, @imp wrote:

I'd like to echo the user requirement. I have not current need for it at $WORK, but can see a need in the future. Having the ability for users to tie into the startup, running as themselves, much like we allow users to add jobs to cron, at, etc would be a useful, but not gating, feature.

I believe that user units is actually an orthogonal feature of the system service manager. If we are to implement user units, IMO, it should be a cross-platform project that consumes pristine systemd unit descriptions and hooks into the OS via PAM. It doesn't even has to be a part of base, because only uses of user units I stumbled upon so far were coming from ports.

I'm working on a systemd-logind replacement, which will fill the user units hole nicely, but there is a ton of work to do.

we are interested in 2 kind of things here, user units which are tight to "sessions" covert your use case and I don't plan into adding them (they fall into the logindish thing you are working on). but there are plenty of other non desktop user units which might be useful and those I am planning on working on after rcd hits the tree and yes there is a tiny overlap between those 2.

sbin/rcctl/rcctl.c
262

first we would not have boost in base anyway, and second this is clearly subjective, as it is really not complex, and if needed wr can make it simple with a static list external, this kind of comment is not useful.

sbin/rcd/hash.c
8–11

how constructive

actually properly read ziaee's comments

reap hard; this time I hope it should be fine for your case @otis

Add boot time looging by popular demand

reap hard; this time I hope it should be fine for your case @otis

no, it did not.

excerpt:

# /sbin/rcd -v
rcd starting
rcd: daemonized (pid 22)
WARNING: control bindat: Read-only file system
WARNING: control socket init failed
started dumpon (pid 45, procdesc fd 5)
started sysctl (pid 46, procdesc fd 6)
started localpkg (pid 47, procdesc fd 7)
started zfs (pid 48, procdesc fd 8)
localpkg exited (status 256, pid 47)
sysctl exited (status 0, pid 46)
started hostid (pid 153, procdesc fd 6)
dumpon exited (status 0, pid 45)
started disks (pid 159, procdesc fd 5)
hostid exited (status 0, pid 153)
disks exited (status 0, pid 159)
started swap (pid 207, procdesc fd 5)
swap exited (status 0, pid 207)
started fsck (pid 232, procdesc fd 5)
fsck exited (status 0, pid 232)
started root (pid 256, procdesc fd 5)
root exited (status 0, pid 256)
started mdconfig (pid 283, procdesc fd 5)
started serial (pid 284, procdesc fd 6)
serial exited (status 0, pid 284)
zfs exited (status 0, pid 48)
mdconfig exited (status 256, pid 283)
started mountcritlocal (pid 312, procdesc fd 5)
mountcritlocal exited (status 0, pid 312)
started kldxref (pid 338, procdesc fd 5)
started var_run (pid 339, procdesc fd 6)
started var (pid 340, procdesc fd 7)
started tmp (pid 341, procdesc fd 8)
var_run exited (status 0, pid 339)
var exited (status 0, pid 340)

the sources are positively current. I can provide an access to that system, to help debugging.

Handle cases when a user explicitly set /var/run a tmpfs in the fstab

more work on zombies
show resources for legacy scripts

more work on zombies
show resources for legacy scripts

This works for me. thanks! No more zombies.

Will it help sshd to start without being indefinitely blocked by e.g. mysqld? That would be enough of a reason for me personally to switch to this thing immediately.

Will it help sshd to start without being indefinitely blocked by e.g. mysqld? That would be enough of a reason for me personally to switch to this thing immediately.

Can you elaborate on this?

Will it help sshd to start without being indefinitely blocked by e.g. mysqld? That would be enough of a reason for me personally to switch to this thing immediately.

Can you elaborate on this?

Sure:

$ rcorder /etc/rc.d/* /usr/local/etc/rc.d/* | egrep -n "sshd|usr"
29:/usr/local/etc/rc.d/microcode_update
108:/usr/local/etc/rc.d/git_daemon
114:/usr/local/etc/rc.d/xrdp
115:/usr/local/etc/rc.d/transmission
116:/usr/local/etc/rc.d/syncthing-relaysrv
117:/usr/local/etc/rc.d/syncthing-relaypoolsrv
118:/usr/local/etc/rc.d/syncthing-discosrv
119:/usr/local/etc/rc.d/syncthing
120:/usr/local/etc/rc.d/smartd
121:/usr/local/etc/rc.d/poudriered
122:/usr/local/etc/rc.d/miniupnpc
123:/usr/local/etc/rc.d/dbus
131:/etc/rc.d/sshd
133:/usr/local/etc/rc.d/lightdm
<CUT>

So whatever <131 potentially hangs, I've got no ssh into machine to fix/kill it as rc is sequential and depends are uncontrollable (because every port may install rc script and put it wherever maintainer decided). I, personally, suffered of this issue before; and the issue is probably as old as rc itself.

improve experience when dealing with legacy script:
better output of the rcctl command, and fix all bugs dealing with local_unbound
reported so far

Will it help sshd to start without being indefinitely blocked by e.g. mysqld? That would be enough of a reason for me personally to switch to this thing immediately.

yes

sbin/rcd/rcd.c
26

Why two blocks of sys/ includes?

782

An error from setsid() should be handled.

It is arguably useful to chdir("/") there just in case.

sbin/rcd/control.c
1135

I suspect this is not robust. If the client is slow or plain malicious it could issue slightly less then 5-secs write per byte essentially hanging the rcd main daemon. Do I miss some mechanism that prevents that?

sbin/rcd/rcd-exec.c
320

Why is LIBPATH dangerous?

And why there is such concept as dangerous env vars? What if I need or want to specify some LD var?

620

IMO it is worth to check with PROC_REAP_STATUS if anybody is left and at least report it.

715

BTW using waitpid only allows to get low 8 bits of the full 32bit exit status. Using e.g. waitid(2) makes it possible to fetch the full status word.