Bug 296542 - local_unbound silently loses all syslog logging after a syslogd restart (chroot has no log socket)
Summary: local_unbound silently loses all syslog logging after a syslogd restart (chro...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 15.1-RELEASE
Hardware: Any Any
: --- Affects Many People
Assignee: Dag-Erling Smørgrav
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-07-05 17:59 UTC by Delta Regeer
Modified: 2026-07-10 15:05 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Delta Regeer 2026-07-05 17:59:44 UTC
`local_unbound` as configured by `local-unbound-setup(8)` runs chrooted to
`/var/unbound` (the setup script defaults `chrootdir="${workdir}"`,
`usr.sbin/unbound/setup/local-unbound-setup.sh` `set_chrootdir()`), and logs
via `syslog(3)` (unbound's `use-syslog` defaults to yes). The syslog
connection is established before the chroot, so logging works — until
`syslogd` is restarted. At that point the connected datagram socket to the
(now unlinked) `/var/run/log` goes stale; libc's automatic reconnect
(`lib/libc/gen/syslog.c`, "syslogd was restarted. In this case make one
(only) attempt to reconnect") resolves `_PATH_LOG` **inside the chroot**,
where no socket exists, and the `LOG_CONS` console fallback is equally
unreachable. Every subsequent message from unbound is silently dropped until
the daemon is restarted. There is no error visible anywhere.

The base system already ships the machinery to fix this —
`libexec/rc/rc.d/syslogd` builds extra `-l` listen sockets from
`altlog_proglist` + `${prog}_chrootdir` (rc.conf(5)) — but nothing wires
`local_unbound` into it, and `/etc/defaults/rc.conf` ships
`altlog_proglist=""`.

## Steps to reproduce (stock config, `local_unbound_enable=YES`)

```sh
# healthy: unbound logs (connection made pre-chroot at daemon start)
service local_unbound restart && sleep 1
grep -c 'Restart of unbound' /var/log/messages   # -> n

service syslogd restart && sleep 1

# broken: this reload logs "Restart of unbound ..." at notice level,
# but nothing arrives
service local_unbound reload && sleep 2
grep -c 'Restart of unbound' /var/log/messages   # -> still n

# only a daemon restart recovers (new pre-chroot connection)
service local_unbound restart && sleep 1
grep -c 'Restart of unbound' /var/log/messages   # -> n+1
```

Observed on a live resolver: after a syslogd restart, `procstat -f <pid>`
shows unbound's `root` vnode at `/var/unbound`, and reload/validation
messages never reach `/var/log/messages`.

## Verified workaround (uses existing rc machinery)

```sh
mkdir -p /var/unbound/var/run     # rc.d/syslogd does not create the dir
sysrc altlog_proglist="local_unbound" local_unbound_chrootdir="/var/unbound"
service syslogd restart
service local_unbound restart     # once, to recover the current process
```

After this, syslogd listens on `/var/unbound/var/run/log`
(`srw-rw-rw- root unbound`), and libc's in-chroot reconnect succeeds:
verified that unbound messages flow again immediately after further syslogd
restarts. (Per libc's one-shot reconnect semantics, the single message that
races a restart can still be lost — same as for any non-chrooted daemon.)

## Suggested fix

Wire the existing pieces together out of the box, e.g.:

1. `/etc/defaults/rc.conf`: `local_unbound_chrootdir="/var/unbound"`, and
   include `local_unbound` in the default `altlog_proglist` (the
   `rc.d/syslogd` loop already gates each entry on `checkyesno
   ${_l}_enable`, so it is a no-op unless local_unbound is enabled); and
2. have `rc.d/syslogd` (or `rc.d/local_unbound` via a precmd) create
   `${_ldir}/var/run` before adding the socket, since the directory does not
   exist in a fresh `local-unbound-setup` tree.

Alternatively `local-unbound-setup(8)` could emit the two rc.conf knobs when
it decides to chroot.

## Test evidence environment

All tests on FreeBSD 15.1-RELEASE-p1 amd64 (pkgbase), unbound 1.25.1
(FreeBSD-local-unbound pkg), default `local-unbound-setup` configuration.
truss excerpt of libc reconnect inside the casper service (healthy path,
for contrast):

```
sendto(4,"<13>1 2026-07-05T13:46:27...",90,0,NULL,0) ERR#54 'Connection reset by peer'
socket(PF_LOCAL,SOCK_DGRAM|SOCK_CLOEXEC,0)  = 4
connect(4,{ AF_UNIX "/var/run/log" },106)   = 0
sendto(4,"<13>1 2026-07-05T13:46:27...",90,0,NULL,0) = 90
```

The chrooted unbound never reaches the successful `connect()` because the
path resolves inside `/var/unbound`.