Dev Docs Netatalk Controller Daemon

Netatalk Controller Daemon

Audited against Netatalk d63155e13 on 2026-10-10. This describes the current source checkout; older releases may differ.

Responsibilities and source

The controller in etc/netatalk/netatalk.c starts and supervises afpd, manages service discovery, and optionally starts a private D-Bus daemon for volumes using the Localsearch Spotlight backend. CNID access happens inside AFP workers; the controller does not start CNID daemons.

File Responsibility
etc/netatalk/netatalk.c Startup validation, process supervision, event loop, reload and shutdown
etc/netatalk/afp_zeroconf.c Zeroconf registration facade
etc/netatalk/afp_avahi.c Avahi implementation
etc/netatalk/afp_mdns.c mDNSResponder implementation
etc/netatalk/meson.build Controller build and dependencies
libatalk/util/netatalk_conf.c Shared configuration and volume loading

Startup and process state

main() parses command-line options, validates the execution mode, takes the PID-file lock, parses configuration with afp_config_parse(), and loads volume definitions through load_afp_conf_vols(..., LV_ALL). It then starts afpd through run_afpd(), configures the selected event library, performs optional Localsearch setup and Zeroconf registration, and enters the event loop.

run_process() uses fork() and execv(). run_afpd() supplies foreground mode (-d) and the selected configuration (-F), and adds -u in single-user mode.

State PID sentinel
Needs starting or restarting NETATALK_SRV_NEEDED = -1
Optional service disabled NETATALK_SRV_OPTIONAL = 0
Child failed NETATALK_SRV_ERROR, equal to NETATALK_SRV_NEEDED
Running Child PID

The tracked service PIDs are afpd_pid and dbus_pid. The 1-second timer_impl() restarts required services unless shutdown has begun. Restart counters are logged; they do not impose a retry limit. sigchld_impl() reaps children with waitpid() and marks a failed service for restart.

Event library

Meson with-eventloop accepts auto, libevent, or libev; auto prefers libevent. The build links the selected event_lib in etc/netatalk/meson.build.

Both implementations call the same behavior functions:

Function Purpose
timer_impl() Service restart checks
sigchld_impl() Reap children; report whether shutdown can finish
sighup_impl() Volume reload, Zeroconf re-registration, SIGHUP to afpd
sigterm_impl() Enter shutdown and send SIGTERM to services
sigquit_impl() Forward SIGQUIT to services

Libevent wraps these in event_new() callbacks and runs event_base_dispatch(). Libev uses ev_signal/ev_timer watchers and ev_run(). Do not infer the event library from the netatalk binary name.

Reload and shutdown

In normal mode, SIGHUP reloads the controller’s volume list, refreshes Zeroconf registration, and signals afpd. The AFP parent separately reparses its configuration and signals session workers. Reloading a volume list is not equivalent to updating every option of an already-open volume; see Configuration Reload.

SIGTERM sets in_shutdown, suppresses restarts, forwards SIGTERM, and arranges event-loop exit after KILL_GRACETIME (5 seconds). If all tracked children exit sooner, sigchld_impl() allows the loop to finish immediately. After the loop, remaining tracked services receive SIGKILL.

SIGQUIT only forwards SIGQUIT through sigquit_impl(). It does not set in_shutdown or arrange the SIGTERM grace timer; it should not be described as the same shutdown path.

Spotlight

any_volume_uses_localsearch() gates the private D-Bus setup. Enabling Spotlight with only cnid or xapian volumes does not require this daemon.

For Localsearch, the controller establishes the D-Bus and XDG environment and calls set_sl_volumes(). This writes a dedicated dconf database/profile and locks for the indexer’s directory settings, then invokes dconf compile using fork/exec. The implementation no longer configures directories through a shell gsettings command. See etc/netatalk/netatalk.c and the Localsearch dependency selection in meson.build.

Backend query and indexing code resides in etc/spotlight; the AFP worker owns query execution. See Data Flow.

Zeroconf

The facade chooses the compiled Avahi or mDNSResponder implementation. Service type constants in etc/netatalk/afp_zeroconf.h include _afpovertcp._tcp, _adisk._tcp, and _device-info._tcp for AFP, Time Machine, and device identity advertisements.

Configuration determines the announced server name, port, model, and Time Machine volumes. An announcement does not itself create a listener or grant filesystem access.

Single-user mode

--single-user (-u) requires a non-root caller and -P with an absolute PID-file path inside a private directory. validate_singleuser_config() requires:

SIGHUP is ignored in this mode; configuration changes require a restart. run_afpd() passes the mode to the AFP parent, and singleuser_admits_uid() in etc/afpd/auth.c limits login to the calling UID.

The policy is implemented by the controller and shared parser, rather than being just a service-script convention. See test/integration/singleuser_test.sh for integration coverage and Authentication.

Service integration

Platform templates are in distrib/initscripts. Inspect the current template for the relevant platform instead of copying a generated unit with substituted installation paths. For example, distrib/initscripts/systemd.netatalk.service.in describes systemd startup and reload, while distrib/initscripts/macos.netatalk.plist.in starts the controller in foreground mode under launchd.

AppleTalk daemons have independent scripts and are not children supervised by this controller.

Footnotes

Generated from the Netatalk GitHub Wiki

Last updated 2026-10-21