12 cfg = config.services.vdirsyncer;
17 mkKeyValue = mkKeyValueDefault {
18 mkValueString = builtins.toJSON;
24 if cfg'.configFile != null then
27 pkgs.writeText "vdirsyncer-${name}.conf" (
30 general = cfg'.config.general // {
32 if cfg'.config.statusPath == null then "/var/lib/vdirsyncer/${name}" else cfg'.config.statusPath;
35 // (mapAttrs' (name: nameValuePair "pair ${name}") cfg'.config.pairs)
36 // (mapAttrs' (name: nameValuePair "storage ${name}") cfg'.config.storages)
40 userUnitConfig = name: cfg': {
43 User = if cfg'.user == null then "vdirsyncer" else cfg'.user;
44 Group = if cfg'.group == null then "vdirsyncer" else cfg'.group;
46 // (optionalAttrs (cfg'.user == null) {
49 // (optionalAttrs (cfg'.additionalGroups != [ ]) {
50 SupplementaryGroups = cfg'.additionalGroups;
52 // (optionalAttrs (cfg'.config.statusPath == null) {
53 StateDirectory = "vdirsyncer/${name}";
54 StateDirectoryMode = "0700";
59 after = [ "network.target" ];
64 NoNewPrivileges = true;
65 ProtectSystem = "strict";
67 ProtectKernelTunables = true;
68 ProtectKernelModules = true;
69 ProtectControlGroups = true;
70 RestrictNamespaces = true;
71 MemoryDenyWriteExecute = true;
72 RestrictRealtime = true;
73 RestrictSUIDSGID = true;
74 RestrictAddressFamilies = "AF_INET AF_INET6";
75 LockPersonality = true;
82 services.vdirsyncer = {
83 enable = mkEnableOption "vdirsyncer";
85 package = mkPackageOption pkgs "vdirsyncer" { };
88 description = "vdirsyncer job configurations";
89 type = types.attrsOf (
92 enable = (mkEnableOption "this vdirsyncer job") // {
98 type = types.nullOr types.str;
101 User account to run vdirsyncer as, otherwise as a systemd
107 type = types.nullOr types.str;
109 description = "group to run vdirsyncer as";
112 additionalGroups = mkOption {
113 type = types.listOf types.str;
115 description = "additional groups to add the dynamic user to";
118 forceDiscover = mkOption {
122 Run `yes | vdirsyncer discover` prior to `vdirsyncer sync`
126 timerConfig = mkOption {
130 OnUnitActiveSec = "6h";
132 description = "systemd timer configuration";
135 configFile = mkOption {
136 type = types.nullOr types.path;
138 description = "existing configuration file";
142 statusPath = mkOption {
143 type = types.nullOr types.str;
145 defaultText = literalExpression "/var/lib/vdirsyncer/\${attrName}";
146 description = "vdirsyncer's status path";
152 description = "general configuration";
156 type = types.attrsOf types.attrs;
158 description = "vdirsyncer pair configurations";
159 example = literalExpression ''
162 a = "my_cloud_contacts";
163 b = "my_local_contacts";
164 collections = [ "from a" ];
165 conflict_resolution = "a wins";
166 metadata = [ "color" "displayname" ];
172 storages = mkOption {
173 type = types.attrsOf types.attrs;
175 description = "vdirsyncer storage configurations";
176 example = literalExpression ''
178 my_cloud_contacts = {
180 url = "https://dav.example.com/";
183 "password.fetch" = [ "command" "cat" "/etc/vdirsyncer/cloud.passwd" ];
185 my_local_contacts = {
187 url = "https://localhost/";
189 "password.fetch" = [ "command" "cat" "/etc/vdirsyncer/local.passwd" ];
202 config = mkIf cfg.enable {
203 systemd.services = mapAttrs' (
205 nameValuePair "vdirsyncer@${name}" (
206 foldr recursiveUpdate { } [
208 (userUnitConfig name cfg')
210 description = "synchronize calendars and contacts (${name})";
211 environment.VDIRSYNCER_CONFIG = toConfigFile name cfg';
212 serviceConfig.ExecStart =
213 (optional cfg'.forceDiscover (
214 pkgs.writeShellScript "vdirsyncer-discover-yes" ''
216 yes | ${cfg.package}/bin/vdirsyncer discover
219 ++ [ "${cfg.package}/bin/vdirsyncer sync" ];
223 ) (filterAttrs (name: cfg': cfg'.enable) cfg.jobs);
225 systemd.timers = mapAttrs' (
227 nameValuePair "vdirsyncer@${name}" {
228 wantedBy = [ "timers.target" ];
229 description = "synchronize calendars and contacts (${name})";
230 inherit (cfg') timerConfig;