1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.ejabberd; 8 9 ctlcfg = pkgs.writeText "ejabberdctl.cfg" '' 10 ERL_EPMD_ADDRESS=127.0.0.1 11 ${cfg.ctlConfig} 12 ''; 13 14 ectl = ''${cfg.package}/bin/ejabberdctl ${if cfg.configFile == null then "" else "--config ${cfg.configFile}"} --ctl-config "${ctlcfg}" --spool "${cfg.spoolDir}" --logs "${cfg.logsDir}"''; 15 16 dumps = lib.concatMapStringsSep " " lib.escapeShellArg cfg.loadDumps; 17 18in { 19 20 ###### interface 21 22 options = { 23 24 services.ejabberd = { 25 26 enable = mkOption { 27 type = types.bool; 28 default = false; 29 description = "Whether to enable ejabberd server"; 30 }; 31 32 package = mkOption { 33 type = types.package; 34 default = pkgs.ejabberd; 35 defaultText = "pkgs.ejabberd"; 36 description = "ejabberd server package to use"; 37 }; 38 39 user = mkOption { 40 type = types.str; 41 default = "ejabberd"; 42 description = "User under which ejabberd is ran"; 43 }; 44 45 group = mkOption { 46 type = types.str; 47 default = "ejabberd"; 48 description = "Group under which ejabberd is ran"; 49 }; 50 51 spoolDir = mkOption { 52 type = types.path; 53 default = "/var/lib/ejabberd"; 54 description = "Location of the spooldir of ejabberd"; 55 }; 56 57 logsDir = mkOption { 58 type = types.path; 59 default = "/var/log/ejabberd"; 60 description = "Location of the logfile directory of ejabberd"; 61 }; 62 63 configFile = mkOption { 64 type = types.nullOr types.path; 65 description = "Configuration file for ejabberd in YAML format"; 66 default = null; 67 }; 68 69 ctlConfig = mkOption { 70 type = types.lines; 71 default = ""; 72 description = "Configuration of ejabberdctl"; 73 }; 74 75 loadDumps = mkOption { 76 type = types.listOf types.path; 77 default = []; 78 description = "Configuration dumps that should be loaded on the first startup"; 79 example = literalExample "[ ./myejabberd.dump ]"; 80 }; 81 }; 82 83 }; 84 85 86 ###### implementation 87 88 config = mkIf cfg.enable { 89 environment.systemPackages = [ cfg.package ]; 90 91 users.extraUsers = optionalAttrs (cfg.user == "ejabberd") (singleton 92 { name = "ejabberd"; 93 group = cfg.group; 94 home = cfg.spoolDir; 95 createHome = true; 96 uid = config.ids.uids.ejabberd; 97 }); 98 99 users.extraGroups = optionalAttrs (cfg.group == "ejabberd") (singleton 100 { name = "ejabberd"; 101 gid = config.ids.gids.ejabberd; 102 }); 103 104 systemd.services.ejabberd = { 105 description = "ejabberd server"; 106 wantedBy = [ "multi-user.target" ]; 107 after = [ "network.target" ]; 108 path = [ pkgs.findutils pkgs.coreutils ]; 109 110 serviceConfig = { 111 Type = "forking"; 112 # FIXME: runit is used for `chpst` -- can we get rid of this? 113 ExecStop = ''${pkgs.runit}/bin/chpst -u "${cfg.user}:${cfg.group}" ${ectl} stop''; 114 ExecReload = ''${pkgs.runit}/bin/chpst -u "${cfg.user}:${cfg.group}" ${ectl} reload_config''; 115 User = cfg.user; 116 Group = cfg.group; 117 PermissionsStartOnly = true; 118 }; 119 120 preStart = '' 121 mkdir -p -m750 "${cfg.logsDir}" 122 chown "${cfg.user}:${cfg.group}" "${cfg.logsDir}" 123 124 mkdir -p -m750 "/var/lock/ejabberdctl" 125 chown "${cfg.user}:${cfg.group}" "/var/lock/ejabberdctl" 126 127 mkdir -p -m750 "${cfg.spoolDir}" 128 chown -R "${cfg.user}:${cfg.group}" "${cfg.spoolDir}" 129 ''; 130 131 script = '' 132 [ -z "$(ls -A '${cfg.spoolDir}')" ] && firstRun=1 133 134 ${ectl} start 135 136 count=0 137 while ! ${ectl} status >/dev/null 2>&1; do 138 if [ $count -eq 30 ]; then 139 echo "ejabberd server hasn't started in 30 seconds, giving up" 140 exit 1 141 fi 142 143 count=$((count++)) 144 sleep 1 145 done 146 147 if [ -n "$firstRun" ]; then 148 for src in ${dumps}; do 149 find "$src" -type f | while read dump; do 150 echo "Loading configuration dump at $dump" 151 ${ectl} load "$dump" 152 done 153 done 154 fi 155 ''; 156 }; 157 158 security.pam.services.ejabberd = {}; 159 160 }; 161 162}