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