1# Avahi daemon.
2{ config, lib, utils, pkgs, ... }:
3
4with lib;
5
6let
7
8 cfg = config.services.avahi;
9
10 # We must escape interfaces due to the systemd interpretation
11 subsystemDevice = interface:
12 "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device";
13
14 avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
15 [server]
16 ${# Users can set `networking.hostName' to the empty string, when getting
17 # a host name from DHCP. In that case, let Avahi take whatever the
18 # current host name is; setting `host-name' to the empty string in
19 # `avahi-daemon.conf' would be invalid.
20 if hostName != ""
21 then "host-name=${hostName}"
22 else ""}
23 browse-domains=${concatStringsSep ", " browseDomains}
24 use-ipv4=${if ipv4 then "yes" else "no"}
25 use-ipv6=${if ipv6 then "yes" else "no"}
26 ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
27 ${optionalString (domainName!=null) "domain-name=${domainName}"}
28
29 [wide-area]
30 enable-wide-area=${if wideArea then "yes" else "no"}
31
32 [publish]
33 disable-publishing=${if publish.enable then "no" else "yes"}
34 disable-user-service-publishing=${if publish.userServices then "no" else "yes"}
35 publish-addresses=${if publish.userServices || publish.addresses then "yes" else "no"}
36 publish-hinfo=${if publish.hinfo then "yes" else "no"}
37 publish-workstation=${if publish.workstation then "yes" else "no"}
38 publish-domain=${if publish.domain then "yes" else "no"}
39 '';
40
41in
42
43{
44
45 ###### interface
46
47 options = {
48
49 services.avahi = {
50
51 enable = mkOption {
52 default = false;
53 description = ''
54 Whether to run the Avahi daemon, which allows Avahi clients
55 to use Avahi's service discovery facilities and also allows
56 the local machine to advertise its presence and services
57 (through the mDNS responder implemented by `avahi-daemon').
58 '';
59 };
60
61 hostName = mkOption {
62 type = types.str;
63 description = ''
64 Host name advertised on the LAN. If not set, avahi will use the value
65 of config.networking.hostName.
66 '';
67 };
68
69 domainName = mkOption {
70 type = types.str;
71 default = "local";
72 description = ''
73 Domain name for all advertisements.
74 '';
75 };
76
77 browseDomains = mkOption {
78 default = [ "0pointer.de" "zeroconf.org" ];
79 description = ''
80 List of non-local DNS domains to be browsed.
81 '';
82 };
83
84 ipv4 = mkOption {
85 default = true;
86 description = ''Whether to use IPv4'';
87 };
88
89 ipv6 = mkOption {
90 default = false;
91 description = ''Whether to use IPv6'';
92 };
93
94 interfaces = mkOption {
95 type = types.nullOr (types.listOf types.str);
96 default = null;
97 description = ''
98 List of network interfaces that should be used by the <command>avahi-daemon</command>.
99 Other interfaces will be ignored. If <literal>null</literal> all local interfaces
100 except loopback and point-to-point will be used.
101 '';
102 };
103
104 wideArea = mkOption {
105 default = true;
106 description = ''Whether to enable wide-area service discovery.'';
107 };
108
109 publish = {
110 enable = mkOption {
111 default = false;
112 description = ''Whether to allow publishing in general.'';
113 };
114
115 userServices = mkOption {
116 default = false;
117 description = ''Whether to publish user services. Will set <literal>addresses=true</literal>.'';
118 };
119
120 addresses = mkOption {
121 default = false;
122 description = ''Whether to register mDNS address records for all local IP addresses.'';
123 };
124
125 hinfo = mkOption {
126 default = false;
127 description = ''
128 Whether to register an mDNS HINFO record which contains information about the
129 local operating system and CPU.
130 '';
131 };
132
133 workstation = mkOption {
134 default = false;
135 description = ''Whether to register a service of type "_workstation._tcp" on the local LAN.'';
136 };
137
138 domain = mkOption {
139 default = false;
140 description = ''Whether to announce the locally used domain name for browsing by other hosts.'';
141 };
142
143 };
144
145 nssmdns = mkOption {
146 default = false;
147 description = ''
148 Whether to enable the mDNS NSS (Name Service Switch) plug-in.
149 Enabling it allows applications to resolve names in the `.local'
150 domain by transparently querying the Avahi daemon.
151 '';
152 };
153
154 };
155
156 };
157
158
159 ###### implementation
160
161 config = mkIf cfg.enable {
162
163 services.avahi.hostName = mkDefault config.networking.hostName;
164
165 users.extraUsers = singleton
166 { name = "avahi";
167 uid = config.ids.uids.avahi;
168 description = "`avahi-daemon' privilege separation user";
169 home = "/var/empty";
170 };
171
172 users.extraGroups = singleton
173 { name = "avahi";
174 gid = config.ids.gids.avahi;
175 };
176
177 system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
178
179 environment.systemPackages = [ pkgs.avahi ];
180
181 systemd.services.avahi-daemon =
182 let
183 deps = optionals (cfg.interfaces!=null) (map subsystemDevice cfg.interfaces);
184 in
185 { description = "Avahi daemon";
186 wantedBy = [ "ip-up.target" ];
187 bindsTo = deps;
188 after = deps;
189 before = [ "ip-up.target" ];
190 # Receive restart event after resume
191 partOf = [ "post-resume.target" ];
192
193 path = [ pkgs.coreutils pkgs.avahi ];
194
195 preStart = "mkdir -p /var/run/avahi-daemon";
196
197 script =
198 ''
199 # Make NSS modules visible so that `avahi_nss_support ()' can
200 # return a sensible value.
201 export LD_LIBRARY_PATH="${config.system.nssModules.path}"
202
203 exec ${pkgs.avahi}/sbin/avahi-daemon --syslog -f "${avahiDaemonConf}"
204 '';
205 };
206
207 services.dbus.enable = true;
208 services.dbus.packages = [ pkgs.avahi ];
209
210 # Enabling Avahi without exposing it in the firewall doesn't make
211 # sense.
212 networking.firewall.allowedUDPPorts = [ 5353 ];
213
214 };
215
216}