Dev Docs Netatalk Controller Daemon
Netatalk Controller Daemon
Audited against Netatalk
d63155e13on 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:
- A caller-owned configuration file, not writable by group or others, in a caller-owned mode-0700 directory.
uam list = uams_srp.soand a private SRP verifier directory containing the caller’s verifier.- At least one static volume, all configured volumes successfully loaded, and SQLite CNID for every volume.
- Volume and CNID paths usable by the caller, with user-owned CNID state.
- No AppleTalk, afpstats, Homes volumes, admin identity, or forced user/group identities.
- Spotlight volumes to use only the
cnidbackend.
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