1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.rsyncd;
7 settingsFormat = pkgs.formats.ini { };
8 configFile = settingsFormat.generate "rsyncd.conf" cfg.settings;
9in {
10 options = {
11 services.rsyncd = {
12
13 enable = mkEnableOption (lib.mdDoc "the rsync daemon");
14
15 port = mkOption {
16 default = 873;
17 type = types.port;
18 description = lib.mdDoc "TCP port the daemon will listen on.";
19 };
20
21 settings = mkOption {
22 inherit (settingsFormat) type;
23 default = { };
24 example = {
25 global = {
26 uid = "nobody";
27 gid = "nobody";
28 "use chroot" = true;
29 "max connections" = 4;
30 };
31 ftp = {
32 path = "/var/ftp/./pub";
33 comment = "whole ftp area";
34 };
35 cvs = {
36 path = "/data/cvs";
37 comment = "CVS repository (requires authentication)";
38 "auth users" = [ "tridge" "susan" ];
39 "secrets file" = "/etc/rsyncd.secrets";
40 };
41 };
42 description = lib.mdDoc ''
43 Configuration for rsyncd. See
44 {manpage}`rsyncd.conf(5)`.
45 '';
46 };
47
48 socketActivated = mkOption {
49 default = false;
50 type = types.bool;
51 description =
52 lib.mdDoc "If enabled Rsync will be socket-activated rather than run persistently.";
53 };
54
55 };
56 };
57
58 imports = (map (option:
59 mkRemovedOptionModule [ "services" "rsyncd" option ]
60 "This option was removed in favor of `services.rsyncd.settings`.") [
61 "address"
62 "extraConfig"
63 "motd"
64 "user"
65 "group"
66 ]);
67
68 config = mkIf cfg.enable {
69
70 services.rsyncd.settings.global.port = toString cfg.port;
71
72 systemd = let
73 serviceConfigSecurity = {
74 ProtectSystem = "full";
75 PrivateDevices = "on";
76 NoNewPrivileges = "on";
77 };
78 in {
79 services.rsync = {
80 enable = !cfg.socketActivated;
81 aliases = [ "rsyncd.service" ];
82
83 description = "fast remote file copy program daemon";
84 after = [ "network.target" ];
85 documentation = [ "man:rsync(1)" "man:rsyncd.conf(5)" ];
86
87 serviceConfig = serviceConfigSecurity // {
88 ExecStart =
89 "${pkgs.rsync}/bin/rsync --daemon --no-detach --config=${configFile}";
90 RestartSec = 1;
91 };
92
93 wantedBy = [ "multi-user.target" ];
94 };
95
96 services."rsync@" = {
97 description = "fast remote file copy program daemon";
98 after = [ "network.target" ];
99
100 serviceConfig = serviceConfigSecurity // {
101 ExecStart = "${pkgs.rsync}/bin/rsync --daemon --config=${configFile}";
102 StandardInput = "socket";
103 StandardOutput = "inherit";
104 StandardError = "journal";
105 };
106 };
107
108 sockets.rsync = {
109 enable = cfg.socketActivated;
110
111 description = "socket for fast remote file copy program daemon";
112 conflicts = [ "rsync.service" ];
113
114 listenStreams = [ (toString cfg.port) ];
115 socketConfig.Accept = true;
116
117 wantedBy = [ "sockets.target" ];
118 };
119 };
120
121 };
122
123 meta.maintainers = with lib.maintainers; [ ehmry ];
124
125 # TODO: socket activated rsyncd
126
127}