Skip to content
FreeBSDDeep Dive Published Updated 8 min readViews unavailable

FreeBSD inetd: Service Activation, Configuration, and Runtime Checks

Operate FreeBSD inetd as a socket-activated service manager: understand entry fields, wait semantics, address binding, reloads, and failure diagnosis.

FreeBSD’s inetd is a network super-server. Rather than keeping a separate daemon running for every low-demand protocol, inetd can listen on configured sockets and start a designated server when a request arrives. It then hands that server the accepted socket as standard input, output, and error. After the child exits, inetd resumes listening according to the service’s mode. This model saves idle processes for some workloads, but it adds a process-start boundary and does not make a service automatically scalable or correct.

inetd is best understood as a dispatcher and lifecycle wrapper. It reads /etc/inetd.conf, resolves service names, creates or binds sockets, and launches server programs. The protocol implementation still belongs to the server. A service that works when started by inetd must correctly handle the inherited descriptors and the environment provided by the parent; many standalone daemons expect to bind their own sockets and are not suitable inetd children without a documented inetd mode.

Understand the configuration record

Each active inetd.conf line describes one endpoint and one server invocation. Fields include service name, socket type, protocol, wait/nowait behavior, user and optional group or login class, server program, and arguments. Fields are separated by whitespace; a comment begins with # at the start of a line. The service name normally resolves through /etc/services, while protocol and socket type must match the service’s documented contract.

The Handbook’s default FTP entry illustrates the shape of a record:

# ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l

It is intentionally commented here. This is a format example from the system configuration, not a recommendation to enable an old service. A copied line may start a different implementation than an operator expects if package files or service databases changed. Before enabling any entry, read the server’s own manual and verify the executable path, user, arguments, protocol, address, and service port.

For a TCP stream service, the accepted connection is a stream socket. For a datagram service, a process may receive a datagram socket under different wait semantics; inetd.conf(5) requires datagram services to use wait, because the server normally reads multiple datagrams from the socket. inetd also supports internal services and TCPMUX. RPC entries have a different service-name/version form. Do not change the protocol from tcp to udp, or select wait based on intuition; use the service documentation and inetd.conf(5) manual.

The wait field is an execution model, not a user priority. A nowait service normally allows inetd to continue accepting requests while children handle established sessions. A wait service gives a child control of the service socket and pauses further dispatch for that service until the child exits. The correct choice depends on whether the service is designed to process one connection per invocation or to handle multiple requests itself. A mismatch can cause serial service, concurrency explosions, or unexpected connection behavior.

Verify the service database and listener

Before changing a record, resolve the service name and port using the local service database:

grep -E '^[[:space:]]*daytime[[:space:]]' /etc/services
grep -v '^[[:space:]]*#' /etc/inetd.conf
service inetd status
sockstat -4 -6 -l

The first command is an example query; not every system has an active daytime entry. The service database may contain multiple aliases or protocol lines, so verify the exact transport and port. sockstat shows listeners, not which configuration line produced them or whether a server can complete a request. Correlate output with inetd’s current config, process state, and a controlled client test.

An entry’s server path must exist and be executable. Arguments are passed according to inetd’s documented field parsing, so preserve required argv[0] conventions from the service manual. A server may need to know whether it was started by inetd or stand-alone. If it tries to bind a second socket while inetd already owns the listener, it may fail immediately or leave a misleading process error.

IPv4 and IPv6 protocol names should be explicit where required. inetd supports address binding through its -a option; a hostname can resolve to an address, but a dual-stack service may need separate tcp4 and tcp6 records. A wildcard listener can be available on more interfaces than intended. Confirm the actual sockets with sockstat after startup rather than assuming a config’s service name identifies its exposure.

Configure startup and reload changes

The rc system controls whether inetd starts at boot. The Handbook documents enabling it with inetd_enable in /etc/rc.conf, starting it through service, and notifying it of changes with service inetd reload. Use sysrc to make a targeted rc.conf update and preserve existing settings:

sysrc inetd_enable=YES
service inetd start
service inetd status
service inetd reload

Do not reload before reviewing the configuration diff. A reload applies every active line, not only the one just edited. Keep a copy of the previous file, validate paths and service names, and know how to restore it. If inetd is stopped, reload may not start it; use the rc service action that matches the current state.

inetd also accepts command-line flags through the rc.conf inetd_flags variable. Flags can choose an address, alter connection logging, enable debugging, and set rate or concurrency limits. Read inetd(8) for each flag and check the effective process arguments before relying on it. Limits expressed by global flags can interact with per-service limits, so record which layer owns the policy and test the expected behavior.

The daemon’s debug mode can help in a staging instance or a maintenance session, but running a second inetd against the same ports can conflict with the production process. Do not launch an ad hoc foreground daemon on a live host merely to see more logs. Prefer existing syslog output, service status, and a controlled configuration reload; isolate any foreground experiment in a VM or on an unused address and port.

Observe child process behavior

After a request, inspect the child process, its credentials, arguments, open descriptors, and exit status. A listener that appears in sockstat only confirms a socket is bound. It does not prove that the child executable launches or that the protocol exchange succeeds. System logs and the server’s own logs may identify an invalid path, missing library, rejected argument, permission problem, or child exit.

For nowait services, measure simultaneous child count under representative arrival bursts. A service can be inexpensive while idle but costly when every request creates a process. The inetd manual documents global and per-service options for maximum children and per-address rates. These are load controls, not a replacement for application-level timeouts or capacity testing. An overly tight limit may reject legitimate clients; an unlimited setting may let a burst consume resources.

For wait services, establish whether a child intentionally owns the socket for the duration of a session. A long-lived child can prevent inetd from dispatching new work on that service. Monitor child lifetime and request latency, and confirm that shutdown does not leave orphan processes. The best evidence is a controlled request with a known duration and a process trace that shows the expected handoff.

inetd can manage Unix-domain sockets as well as Internet sockets. Such entries use a unix protocol and an absolute path in the service-name field, with an optional owner, group, and mode prefix. These records have filesystem lifecycle concerns, including stale socket paths and permissions, so do not treat them as ordinary TCP entries. Validate the path and process cleanup behavior with the service’s own documentation.

Diagnose common failures

If no listener appears, confirm inetd is enabled and running, the line is not commented, the service resolves in /etc/services, and the requested address exists on the host. A typo in the transport or socket type can prevent a valid bind. Inspect startup output and system logs instead of repeatedly restarting the daemon.

If the listener exists but a client cannot connect, compare the requested address family, protocol, and port with sockstat. Then run a client from the same host and from the expected network location. Routing, a local firewall, upstream filtering, and client address-family preference can each produce a failure outside inetd’s process activation path. The listener view alone cannot distinguish those causes.

If a connection arrives but the server exits, check executable path, file permissions, dynamic library availability, arguments, login class, service user, and whether the program expects an inherited socket. Confirm its invocation contract before changing the user field or switching to nowait. A server that requires a private daemon process model may need to run stand-alone instead of through inetd.

If reload appears ineffective, verify the file path, whether the rc script signals the expected inetd PID, and whether the new listener or child behavior differs. Keep the prior configuration ready for rollback. For a critical endpoint, test both a positive request and a negative or malformed request in staging, with logs from inetd and the server captured together.

When inetd is not the right manager

A dedicated service is usually preferable when the daemon maintains significant shared state, needs complex restart supervision, handles sustained high connection rates, or needs its own readiness and health protocol. inetd is not a general service manager: it does not replace rc.d dependency ordering, application watchdogs, a job queue, or a protocol proxy.

For an occasionally used, correctly designed child server, inetd can reduce idle daemon overhead and centralize listening sockets. Keep the configuration short, make every active line intentional, and document whether the service is IPv4, IPv6, TCP, UDP, or local-only. Reassess legacy entries during upgrades because package and base-system implementations may change.

Production acceptance should demonstrate that inetd starts at the expected boot stage, binds only the intended endpoints, reloads the approved configuration, launches the correct child with the intended credentials and arguments, respects concurrency expectations, logs failures, and stops cleanly. A single successful connection is only the beginning of that proof.

Related:

Sources:

Comments