{ config, lib, pkgs, ... }: with lib; let cfg = config.services.asterisk; asteriskUser = "asterisk"; varlibdir = "/var/lib/asterisk"; spooldir = "/var/spool/asterisk"; logdir = "/var/log/asterisk"; asteriskEtc = pkgs.stdenv.mkDerivation ((mapAttrs' (name: value: nameValuePair # Fudge the names to make bash happy ((replaceChars ["."] ["_"] name) + "_") (value) ) cfg.confFiles) // { confFilesString = concatStringsSep " " ( attrNames cfg.confFiles ); name = "asterisk.etc"; # Default asterisk.conf file # (Notice that astetcdir will be set to the path of this derivation) asteriskConf = '' [directories] astetcdir => @out@ astmoddir => ${pkgs.asterisk}/lib/asterisk/modules astvarlibdir => /var/lib/asterisk astdbdir => /var/lib/asterisk astkeydir => /var/lib/asterisk astdatadir => /var/lib/asterisk astagidir => /var/lib/asterisk/agi-bin astspooldir => /var/spool/asterisk astrundir => /var/run/asterisk astlogdir => /var/log/asterisk astsbindir => ${pkgs.asterisk}/sbin ''; extraConf = cfg.extraConfig; # Loading all modules by default is considered sensible by the authors of # "Asterisk: The Definitive Guide". Secure sites will likely want to # specify their own "modules.conf" in the confFiles option. modulesConf = '' [modules] autoload=yes ''; # Use syslog for logging so logs can be viewed with journalctl loggerConf = '' [general] [logfiles] syslog.local0 => notice,warning,error ''; buildCommand = '' mkdir -p "$out" # Create asterisk.conf, pointing astetcdir to the path of this derivation echo "$asteriskConf" | sed "s|@out@|$out|g" > "$out"/asterisk.conf echo "$extraConf" >> "$out"/asterisk.conf echo "$modulesConf" > "$out"/modules.conf echo "$loggerConf" > "$out"/logger.conf # Config files specified in confFiles option override all other files for i in $confFilesString; do conf=$(echo "$i"_ | sed 's/\./_/g') echo "''${!conf}" > "$out"/"$i" done ''; }); in { options = { services.asterisk = { enable = mkOption { type = types.bool; default = false; description = '' Whether to enable the Asterisk PBX server. ''; }; extraConfig = mkOption { default = ""; type = types.lines; example = '' [options] verbose=3 debug=3 ''; description = '' Extra configuration options appended to the default asterisk.conf file. ''; }; confFiles = mkOption { default = {}; type = types.attrsOf types.str; example = literalExample '' { "extensions.conf" = ''' [tests] ; Dial 100 for "hello, world" exten => 100,1,Answer() same => n,Wait(1) same => n,Playback(hello-world) same => n,Hangup() [softphones] include => tests [unauthorized] '''; "sip.conf" = ''' [general] allowguest=no ; Require authentication context=unauthorized ; Send unauthorized users to /dev/null srvlookup=no ; Don't do DNS lookup udpbindaddr=0.0.0.0 ; Listen on all interfaces nat=force_rport,comedia ; Assume device is behind NAT [softphone](!) type=friend ; Match on username first, IP second context=softphones ; Send to softphones context in ; extensions.conf file host=dynamic ; Device will register with asterisk disallow=all ; Manually specify codecs to allow allow=g722 allow=ulaw allow=alaw [myphone](softphone) secret=GhoshevFew ; Change this password! '''; "logger.conf" = ''' [general] [logfiles] ; Add debug output to log syslog.local0 => notice,warning,error,debug '''; } ''; description = '' Sets the content of config files (typically ending with .conf) in the Asterisk configuration directory. Note that if you want to change asterisk.conf, it is preferable to use the option over this option. If "asterisk.conf" is specified with the option (not recommended), you must be prepared to set your own astetcdir path. See for more examples of what is possible here. ''; }; extraArguments = mkOption { default = []; type = types.listOf types.str; example = [ "-vvvddd" "-e" "1024" ]; description = '' Additional command line arguments to pass to Asterisk. ''; }; }; }; config = mkIf cfg.enable { users.extraUsers = singleton { name = asteriskUser; uid = config.ids.uids.asterisk; description = "Asterisk daemon user"; home = varlibdir; }; systemd.services.asterisk = { description = '' Asterisk PBX server ''; wantedBy = [ "multi-user.target" ]; preStart = '' # Copy skeleton directory tree to /var for d in '${varlibdir}' '${spooldir}' '${logdir}'; do # TODO: Make exceptions for /var directories that likely should be updated if [ ! -e "$d" ]; then mkdir -p "$d" cp --recursive ${pkgs.asterisk}/"$d" "$d" chown --recursive ${asteriskUser} "$d" find "$d" -type d | xargs chmod 0755 fi done ''; serviceConfig = { ExecStart = let # FIXME: This doesn't account for arguments with spaces argString = concatStringsSep " " cfg.extraArguments; in "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C ${asteriskEtc}/asterisk.conf ${argString} -F"; Type = "forking"; PIDFile = "/var/run/asterisk/asterisk.pid"; }; }; }; }