1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.asterisk;
7
8 asteriskUser = "asterisk";
9 asteriskGroup = "asterisk";
10
11 varlibdir = "/var/lib/asterisk";
12 spooldir = "/var/spool/asterisk";
13 logdir = "/var/log/asterisk";
14
15 # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override
16 defaultConfFiles = subtractLists (attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles;
17 allConfFiles =
18 cfg.confFiles //
19 builtins.listToAttrs (map (x: { name = x;
20 value = builtins.readFile (cfg.package + "/etc/asterisk/" + x); })
21 defaultConfFiles);
22
23 asteriskEtc = pkgs.stdenv.mkDerivation
24 ((mapAttrs' (name: value: nameValuePair
25 # Fudge the names to make bash happy
26 ((replaceChars ["."] ["_"] name) + "_")
27 (value)
28 ) allConfFiles) //
29 {
30 confFilesString = concatStringsSep " " (
31 attrNames allConfFiles
32 );
33
34 name = "asterisk-etc";
35
36 # Default asterisk.conf file
37 # (Notice that astetcdir will be set to the path of this derivation)
38 asteriskConf = ''
39 [directories]
40 astetcdir => /etc/asterisk
41 astmoddir => ${cfg.package}/lib/asterisk/modules
42 astvarlibdir => /var/lib/asterisk
43 astdbdir => /var/lib/asterisk
44 astkeydir => /var/lib/asterisk
45 astdatadir => /var/lib/asterisk
46 astagidir => /var/lib/asterisk/agi-bin
47 astspooldir => /var/spool/asterisk
48 astrundir => /var/run/asterisk
49 astlogdir => /var/log/asterisk
50 astsbindir => ${cfg.package}/sbin
51 '';
52 extraConf = cfg.extraConfig;
53
54 # Loading all modules by default is considered sensible by the authors of
55 # "Asterisk: The Definitive Guide". Secure sites will likely want to
56 # specify their own "modules.conf" in the confFiles option.
57 modulesConf = ''
58 [modules]
59 autoload=yes
60 '';
61
62 # Use syslog for logging so logs can be viewed with journalctl
63 loggerConf = ''
64 [general]
65
66 [logfiles]
67 syslog.local0 => notice,warning,error
68 '';
69
70 buildCommand = ''
71 mkdir -p "$out"
72
73 # Create asterisk.conf, pointing astetcdir to the path of this derivation
74 echo "$asteriskConf" | sed "s|@out@|$out|g" > "$out"/asterisk.conf
75 echo "$extraConf" >> "$out"/asterisk.conf
76
77 echo "$modulesConf" > "$out"/modules.conf
78
79 echo "$loggerConf" > "$out"/logger.conf
80
81 # Config files specified in confFiles option override all other files
82 for i in $confFilesString; do
83 conf=$(echo "$i"_ | sed 's/\./_/g')
84 echo "''${!conf}" > "$out"/"$i"
85 done
86 '';
87 });
88in
89
90{
91 options = {
92 services.asterisk = {
93 enable = mkOption {
94 type = types.bool;
95 default = false;
96 description = ''
97 Whether to enable the Asterisk PBX server.
98 '';
99 };
100
101 extraConfig = mkOption {
102 default = "";
103 type = types.lines;
104 example = ''
105 [options]
106 verbose=3
107 debug=3
108 '';
109 description = ''
110 Extra configuration options appended to the default
111 <literal>asterisk.conf</literal> file.
112 '';
113 };
114
115 confFiles = mkOption {
116 default = {};
117 type = types.attrsOf types.str;
118 example = literalExample
119 ''
120 {
121 "extensions.conf" = '''
122 [tests]
123 ; Dial 100 for "hello, world"
124 exten => 100,1,Answer()
125 same => n,Wait(1)
126 same => n,Playback(hello-world)
127 same => n,Hangup()
128
129 [softphones]
130 include => tests
131
132 [unauthorized]
133 ''';
134 "sip.conf" = '''
135 [general]
136 allowguest=no ; Require authentication
137 context=unauthorized ; Send unauthorized users to /dev/null
138 srvlookup=no ; Don't do DNS lookup
139 udpbindaddr=0.0.0.0 ; Listen on all interfaces
140 nat=force_rport,comedia ; Assume device is behind NAT
141
142 [softphone](!)
143 type=friend ; Match on username first, IP second
144 context=softphones ; Send to softphones context in
145 ; extensions.conf file
146 host=dynamic ; Device will register with asterisk
147 disallow=all ; Manually specify codecs to allow
148 allow=g722
149 allow=ulaw
150 allow=alaw
151
152 [myphone](softphone)
153 secret=GhoshevFew ; Change this password!
154 ''';
155 "logger.conf" = '''
156 [general]
157
158 [logfiles]
159 ; Add debug output to log
160 syslog.local0 => notice,warning,error,debug
161 ''';
162 }
163 '';
164 description = ''
165 Sets the content of config files (typically ending with
166 <literal>.conf</literal>) in the Asterisk configuration directory.
167
168 Note that if you want to change <literal>asterisk.conf</literal>, it
169 is preferable to use the <option>services.asterisk.extraConfig</option>
170 option over this option. If <literal>"asterisk.conf"</literal> is
171 specified with the <option>confFiles</option> option (not recommended),
172 you must be prepared to set your own <literal>astetcdir</literal>
173 path.
174
175 See
176 <link xlink:href="http://www.asterisk.org/community/documentation"/>
177 for more examples of what is possible here.
178 '';
179 };
180
181 useTheseDefaultConfFiles = mkOption {
182 default = [ "ari.conf" "acl.conf" "agents.conf" "amd.conf" "calendar.conf" "cdr.conf" "cdr_syslog.conf" "cdr_custom.conf" "cel.conf" "cel_custom.conf" "cli_aliases.conf" "confbridge.conf" "dundi.conf" "features.conf" "hep.conf" "iax.conf" "pjsip.conf" "pjsip_wizard.conf" "phone.conf" "phoneprov.conf" "queues.conf" "res_config_sqlite3.conf" "res_parking.conf" "statsd.conf" "udptl.conf" "unistim.conf" ];
183 type = types.listOf types.str;
184 example = [ "sip.conf" "dundi.conf" ];
185 description = ''Sets these config files to the default content. The default value for
186 this option contains all necesscary files to avoid errors at startup.
187 This does not override settings via <option>services.asterisk.confFiles</option>.
188 '';
189 };
190
191 extraArguments = mkOption {
192 default = [];
193 type = types.listOf types.str;
194 example =
195 [ "-vvvddd" "-e" "1024" ];
196 description = ''
197 Additional command line arguments to pass to Asterisk.
198 '';
199 };
200 package = mkOption {
201 type = types.package;
202 default = pkgs.asterisk;
203 defaultText = "pkgs.asterisk";
204 description = "The Asterisk package to use.";
205 };
206 };
207 };
208
209 config = mkIf cfg.enable {
210 environment.systemPackages = [ cfg.package ];
211
212 environment.etc.asterisk.source = asteriskEtc;
213
214 users.extraUsers.asterisk =
215 { name = asteriskUser;
216 group = asteriskGroup;
217 uid = config.ids.uids.asterisk;
218 description = "Asterisk daemon user";
219 home = varlibdir;
220 };
221
222 users.extraGroups.asterisk =
223 { name = asteriskGroup;
224 gid = config.ids.gids.asterisk;
225 };
226
227 systemd.services.asterisk = {
228 description = ''
229 Asterisk PBX server
230 '';
231
232 wantedBy = [ "multi-user.target" ];
233
234 # Do not restart, to avoid disruption of running calls. Restart unit by yourself!
235 restartIfChanged = false;
236
237 preStart = ''
238 # Copy skeleton directory tree to /var
239 for d in '${varlibdir}' '${spooldir}' '${logdir}'; do
240 # TODO: Make exceptions for /var directories that likely should be updated
241 if [ ! -e "$d" ]; then
242 mkdir -p "$d"
243 cp --recursive ${cfg.package}/"$d"/* "$d"/
244 chown --recursive ${asteriskUser}:${asteriskGroup} "$d"
245 find "$d" -type d | xargs chmod 0755
246 fi
247 done
248 '';
249
250 serviceConfig = {
251 ExecStart =
252 let
253 # FIXME: This doesn't account for arguments with spaces
254 argString = concatStringsSep " " cfg.extraArguments;
255 in
256 "${cfg.package}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
257 ExecReload = ''${cfg.package}/bin/asterisk -x "core reload"
258 '';
259 Type = "forking";
260 PIDFile = "/var/run/asterisk/asterisk.pid";
261 };
262 };
263 };
264}