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