This is about a fix for a small issue with Dan Bernstein’s daemontools package.

Since daemontools was written, some systems have started using 64 bit inode and device numbers in struct stat.

Unfortunately, svscan.c assumes these values are unsigned long:

struct {
  unsigned long dev;
  unsigned long ino;
  int flagactive;
  int flaglog;
  int pid; /* 0 if not running */
  int pidlog; /* 0 if not running */
  int pi[2]; /* defined if flaglog */
} x[SERVICES];

In the case of recent-ish FreeBSD this can cause problems on 32 bit hardware. On these machines, device and inode numbers are 64 bit, but unsigned long is only 32 bit.

I discovered this while building a FreeBSD 12 jail in a FreeBSD 13 ZFS system on an old 32 bit machine. (I’m sure this is a very common setup.)

Symptoms

When I started svscan the normal way, readproctitle showed errors about not being able to get locks.

Running svscan in the forground shows this:

# svscan /service
supervise: fatal: unable to acquire qmail-smtpsub/supervise/lock: temporary failure
supervise: fatal: unable to acquire log/supervise/lock: temporary failure
. . .

svscan.c scans the /service directory every 5 seconds looking for new services to start, and to reap any exited child processes.

It maintains a table matching each service directory with its corresponding supervise processes. Whenever a service directory is considered for startup, svscan.c calls stat() on the directory and looks for a match in the table. If there is a match, nothing more happens; the service is already running.

But if the device and inode numbers returned by stat() don’t match anything in the table, new instances of supervise are started.

When unsigned long isn’t enough to capture device and inode numbers, no match is found, even when supervise is already running fine. So new instances of supervise are started, and they find they cannot get the locks, and then exit with the ‘unable to acquire lock’ messages.

Fix

The fix is to change the types of .dev and .ino to the standard POSIX types dev_t and ino_t:

struct {
  dev_t dev;
  ino_t ino;
  int flagactive;
  int flaglog;
  int pid; /* 0 if not running */
  int pidlog; /* 0 if not running */
  int pi[2]; /* defined if flaglog */
} x[SERVICES];