1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.asterisk;
9
10 asteriskUser = "asterisk";
11 asteriskGroup = "asterisk";
12
13 varlibdir = "/var/lib/asterisk";
14 spooldir = "/var/spool/asterisk";
15 logdir = "/var/log/asterisk";
16
17 # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override
18 defaultConfFiles = lib.subtractLists (lib.attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles;
19 allConfFiles = {
20 # Default asterisk.conf file
21 "asterisk.conf".text = ''
22 [directories]
23 astetcdir => /etc/asterisk
24 astmoddir => ${cfg.package}/lib/asterisk/modules
25 astvarlibdir => /var/lib/asterisk
26 astdbdir => /var/lib/asterisk
27 astkeydir => /var/lib/asterisk
28 astdatadir => /var/lib/asterisk
29 astagidir => /var/lib/asterisk/agi-bin
30 astspooldir => /var/spool/asterisk
31 astrundir => /run/asterisk
32 astlogdir => /var/log/asterisk
33 astsbindir => ${cfg.package}/sbin
34 ${cfg.extraConfig}
35 '';
36
37 # Loading all modules by default is considered sensible by the authors of
38 # "Asterisk: The Definitive Guide". Secure sites will likely want to
39 # specify their own "modules.conf" in the confFiles option.
40 "modules.conf".text = ''
41 [modules]
42 autoload=yes
43 '';
44
45 # Use syslog for logging so logs can be viewed with journalctl
46 "logger.conf".text = ''
47 [general]
48
49 [logfiles]
50 syslog.local0 => notice,warning,error
51 '';
52 }
53 // lib.mapAttrs (name: text: { inherit text; }) cfg.confFiles
54 // lib.listToAttrs (
55 map (x: lib.nameValuePair x { source = cfg.package + "/etc/asterisk/" + x; }) defaultConfFiles
56 );
57
58in
59
60{
61 options = {
62 services.asterisk = {
63 enable = lib.mkOption {
64 type = lib.types.bool;
65 default = false;
66 description = ''
67 Whether to enable the Asterisk PBX server.
68 '';
69 };
70
71 extraConfig = lib.mkOption {
72 default = "";
73 type = lib.types.lines;
74 example = ''
75 [options]
76 verbose=3
77 debug=3
78 '';
79 description = ''
80 Extra configuration options appended to the default
81 `asterisk.conf` file.
82 '';
83 };
84
85 confFiles = lib.mkOption {
86 default = { };
87 type = lib.types.attrsOf lib.types.str;
88 example = lib.literalExpression ''
89 {
90 "extensions.conf" = '''
91 [tests]
92 ; Dial 100 for "hello, world"
93 exten => 100,1,Answer()
94 same => n,Wait(1)
95 same => n,Playback(hello-world)
96 same => n,Hangup()
97
98 [softphones]
99 include => tests
100
101 [unauthorized]
102 ''';
103 "sip.conf" = '''
104 [general]
105 allowguest=no ; Require authentication
106 context=unauthorized ; Send unauthorized users to /dev/null
107 srvlookup=no ; Don't do DNS lookup
108 udpbindaddr=0.0.0.0 ; Listen on all interfaces
109 nat=force_rport,comedia ; Assume device is behind NAT
110
111 [softphone](!)
112 type=friend ; Match on username first, IP second
113 context=softphones ; Send to softphones context in
114 ; extensions.conf file
115 host=dynamic ; Device will register with asterisk
116 disallow=all ; Manually specify codecs to allow
117 allow=g722
118 allow=ulaw
119 allow=alaw
120
121 [myphone](softphone)
122 secret=GhoshevFew ; Change this password!
123 ''';
124 "logger.conf" = '''
125 [general]
126
127 [logfiles]
128 ; Add debug output to log
129 syslog.local0 => notice,warning,error,debug
130 ''';
131 }
132 '';
133 description = ''
134 Sets the content of config files (typically ending with
135 `.conf`) in the Asterisk configuration directory.
136
137 Note that if you want to change `asterisk.conf`, it
138 is preferable to use the {option}`services.asterisk.extraConfig`
139 option over this option. If `"asterisk.conf"` is
140 specified with the {option}`confFiles` option (not recommended),
141 you must be prepared to set your own `astetcdir`
142 path.
143
144 See
145 <https://www.asterisk.org/community/documentation/>
146 for more examples of what is possible here.
147 '';
148 };
149
150 useTheseDefaultConfFiles = lib.mkOption {
151 default = [
152 "ari.conf"
153 "acl.conf"
154 "agents.conf"
155 "amd.conf"
156 "calendar.conf"
157 "cdr.conf"
158 "cdr_syslog.conf"
159 "cdr_custom.conf"
160 "cel.conf"
161 "cel_custom.conf"
162 "cli_aliases.conf"
163 "confbridge.conf"
164 "dundi.conf"
165 "features.conf"
166 "hep.conf"
167 "iax.conf"
168 "pjsip.conf"
169 "pjsip_wizard.conf"
170 "phone.conf"
171 "phoneprov.conf"
172 "queues.conf"
173 "res_config_sqlite3.conf"
174 "res_parking.conf"
175 "statsd.conf"
176 "udptl.conf"
177 "unistim.conf"
178 ];
179 type = lib.types.listOf lib.types.str;
180 example = [
181 "sip.conf"
182 "dundi.conf"
183 ];
184 description = ''
185 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`.
188 '';
189 };
190
191 extraArguments = lib.mkOption {
192 default = [ ];
193 type = lib.types.listOf lib.types.str;
194 example = [
195 "-vvvddd"
196 "-e"
197 "1024"
198 ];
199 description = ''
200 Additional command line arguments to pass to Asterisk.
201 '';
202 };
203 package = lib.mkPackageOption pkgs "asterisk" { };
204 };
205 };
206
207 config = lib.mkIf cfg.enable {
208 environment.systemPackages = [ cfg.package ];
209
210 environment.etc = lib.mapAttrs' (
211 name: value: lib.nameValuePair "asterisk/${name}" value
212 ) allConfFiles;
213
214 users.users.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.groups.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 = lib.concatStringsSep " " cfg.extraArguments;
255 in
256 "${cfg.package}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
257 ExecReload = ''
258 ${cfg.package}/bin/asterisk -x "core reload"
259 '';
260 Type = "forking";
261 PIDFile = "/run/asterisk/asterisk.pid";
262 };
263 };
264 };
265}