diff options
author | Simon South <simon@simonsouth.net> | 2020-12-05 10:27:55 -0500 |
---|---|---|
committer | 宋文武 <iyzsong@member.fsf.org> | 2021-02-12 15:11:36 +0800 |
commit | db6b9d2f4bc59511904e8c1412d0257675c46095 (patch) | |
tree | bb14d07667f3ae3065cb48b30a64ce8c5059ed44 | |
parent | 8458d8db70885b350285e8b1490734349135e51e (diff) | |
download | guix-db6b9d2f4bc59511904e8c1412d0257675c46095.tar.gz |
services: Add transmission-daemon service.
* gnu/services/file-sharing.scm: New file. * gnu/local.mk (GNU_SYSTEM_MODULES): Add it. * po/packages/POTFILES.in: Add it. * tests/services/file-sharing.scm: New file. * Makefile.am (SCM_TESTS): Add it. * doc/guix.texi (File-Sharing Services): New section. Signed-off-by: 宋文武 <iyzsong@member.fsf.org>
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | doc/guix.texi | 799 | ||||
-rw-r--r-- | gnu/local.mk | 1 | ||||
-rw-r--r-- | gnu/services/file-sharing.scm | 804 | ||||
-rw-r--r-- | po/packages/POTFILES.in | 1 | ||||
-rw-r--r-- | tests/services/file-sharing.scm | 59 |
6 files changed, 1665 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 798808bde6..52537fb53d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -475,6 +475,7 @@ SCM_TESTS = \ tests/scripts.scm \ tests/search-paths.scm \ tests/services.scm \ + tests/services/file-sharing.scm \ tests/services/linux.scm \ tests/sets.scm \ tests/size.scm \ diff --git a/doc/guix.texi b/doc/guix.texi index 8944f5129d..aba8a6b575 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -14716,6 +14716,7 @@ declaration. * Mail Services:: IMAP, POP3, SMTP, and all that. * Messaging Services:: Messaging services. * Telephony Services:: Telephony services. +* File-Sharing Services:: File-sharing services. * Monitoring Services:: Monitoring services. * Kerberos Services:: Kerberos services. * LDAP Services:: LDAP services. @@ -22287,6 +22288,804 @@ If it is set your server will be linked by this host name instead. +@node File-Sharing Services +@subsection File-Sharing Services + +The @code{(gnu services file-sharing)} module provides services that +assist with transferring files over peer-to-peer file-sharing networks. + +@subsubheading Transmission Daemon Service + +@uref{https://transmissionbt.com/, Transmission} is a flexible +BitTorrent client that offers a variety of graphical and command-line +interfaces. A @code{transmission-daemon-service-type} service provides +Transmission's headless variant, @command{transmission-daemon}, as a +system service, allowing users to share files via BitTorrent even when +they are not logged in. + +@deffn {Scheme Variable} transmission-daemon-service-type +The service type for the Transmission Daemon BitTorrent client. Its +value must be a @code{transmission-daemon-configuration} object as in +this example: + +@lisp +(service transmission-daemon-service-type + (transmission-daemon-configuration + ;; Restrict access to the RPC ("control") interface + (rpc-authentication-required? #t) + (rpc-username "transmission") + (rpc-password + (transmission-password-hash + "transmission" ; desired password + "uKd1uMs9")) ; arbitrary salt value + + ;; Accept requests from this and other hosts on the + ;; local network + (rpc-whitelist-enabled? #t) + (rpc-whitelist '("::1" "127.0.0.1" "192.168.0.*")) + + ;; Limit bandwidth use during work hours + (alt-speed-down (* 1024 2)) ; 2 MB/s + (alt-speed-up 512) ; 512 kB/s + + (alt-speed-time-enabled? #t) + (alt-speed-time-day 'weekdays) + (alt-speed-time-begin + (+ (* 60 8) 30)) ; 8:30 am + (alt-speed-time-end + (+ (* 60 (+ 12 5)) 30)))) ; 5:30 pm +@end lisp +@end deffn + +Once the service is started, users can interact with the daemon through +its Web interface (at @code{http://localhost:9091/}) or by using the +@command{transmission-remote} command-line tool, available in the +@code{transmission} package. (Emacs users may want to also consider the +@code{emacs-transmission} package.) Both communicate with the daemon +through its remote procedure call (RPC) interface, which by default is +available to all users on the system; you may wish to change this by +assigning values to the @code{rpc-authentication-required?}, +@code{rpc-username} and @code{rpc-password} settings, as shown in the +example above and documented further below. + +The value for @code{rpc-password} must be a password hash of the type +generated and used by Transmission clients. This can be copied verbatim +from an existing @file{settings.json} file, if another Transmission +client is already being used. Otherwise, the +@code{transmission-password-hash} and @code{transmission-random-salt} +procedures provided by this module can be used to obtain a suitable hash +value. + +@deffn {Scheme Procedure} transmission-password-hash @var{password} @var{salt} +Returns a string containing the result of hashing @var{password} +together with @var{salt}, in the format recognized by Transmission +clients for their @code{rpc-password} configuration setting. + +@var{salt} must be an eight-character string. The +@code{transmission-random-salt} procedure can be used to generate a +suitable salt value at random. +@end deffn + +@deffn {Scheme Procedure} transmission-random-salt +Returns a string containing a random, eight-character salt value of the +type generated and used by Transmission clients, suitable for passing to +the @code{transmission-password-hash} procedure. +@end deffn + +These procedures are accessible from within a Guile REPL started with +the @command{guix repl} command (@pxref {Invoking guix repl}). This is +useful for obtaining a random salt value to provide as the second +parameter to `transmission-password-hash`, as in this example session: + +@example +$ guix repl +scheme@@(guix-user)> ,use (gnu services file-sharing) +scheme@@(guix-user)> (transmission-random-salt) +$1 = "uKd1uMs9" +@end example + +Alternatively, a complete password hash can generated in a single step: + +@example +scheme@@(guix-user)> (transmission-password-hash "transmission" +(transmission-random-salt)) +$2 = "@{c8bbc6d1740cd8dc819a6e25563b67812c1c19c9VtFPfdsX" +@end example + +The resulting string can be used as-is for the value of +@code{rpc-password}, allowing the password to be kept hidden even in the +operating-system configuration. + +Torrent files downloaded by the daemon are directly accessible only to +users in the ``transmission'' user group, who receive read-only access +to the directory specified by the @code{download-dir} configuration +setting (and also the directory specified by @code{incomplete-dir}, if +@code{incomplete-dir-enabled?} is @code{#t}). Downloaded files can be +moved to another directory or deleted altogether using +@command{transmission-remote} with its @code{--move} and +@code{--remove-and-delete} options. + +If the @code{watch-dir-enabled?} setting is set to @code{#t}, users in +the ``transmission'' group are able also to place @file{.torrent} files +in the directory specified by @code{watch-dir} to have the corresponding +torrents added by the daemon. (The @code{trash-original-torrent-files?} +setting controls whether the daemon deletes these files after processing +them.) + +Some of the daemon's configuration settings can be changed temporarily +by @command{transmission-remote} and similar tools. To undo these +changes, use the service's @code{reload} action to have the daemon +reload its settings from disk: + +@example +# herd reload transmission-daemon +@end example + +The full set of available configuration settings is defined by the +@code{transmission-daemon-configuration} data type. + +@deftp {Data Type} transmission-daemon-configuration +The data type representing configuration settings for Transmission +Daemon. These correspond directly to the settings recognized by +Transmission clients in their @file{settings.json} file. +@end deftp + +@c The following documentation was initially generated by +@c (generate-transmission-daemon-documentation) in (gnu services +@c file-sharing). Manually maintained documentation is better, so we +@c shouldn't hesitate to edit below as needed. However if the change +@c you want to make to this documentation can be done in an automated +@c way, it's probably easier to change (generate-documentation) than to +@c make it below and have to deal with the churn as Transmission Daemon +@c updates. + +@c %start of fragment + +Available @code{transmission-daemon-configuration} fields are: + +@deftypevr {@code{transmission-daemon-configuration} parameter} package transmission +The Transmission package to use. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer stop-wait-period +The period, in seconds, to wait when stopping the service for +@command{transmission-daemon} to exit before killing its process. This +allows the daemon time to complete its housekeeping and send a final +update to trackers as it shuts down. On slow hosts, or hosts with a +slow network connection, this value may need to be increased. + +Defaults to @samp{10}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string download-dir +The directory to which torrent files are downloaded. + +Defaults to @samp{"/var/lib/transmission-daemon/downloads"}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean incomplete-dir-enabled? +If @code{#t}, files will be held in @code{incomplete-dir} while their +torrent is being downloaded, then moved to @code{download-dir} once the +torrent is complete. Otherwise, files for all torrents (including those +still being downloaded) will be placed in @code{download-dir}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-string incomplete-dir +The directory in which files from incompletely downloaded torrents will +be held when @code{incomplete-dir-enabled?} is @code{#t}. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} umask umask +The file mode creation mask used for downloaded files. (See the +@command{umask} man page for more information.) + +Defaults to @samp{18}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean rename-partial-files? +When @code{#t}, ``.part'' is appended to the name of partially +downloaded files. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} preallocation-mode preallocation +The mode by which space should be preallocated for downloaded files, one +of @code{none}, @code{fast} (or @code{sparse}) and @code{full}. +Specifying @code{full} will minimize disk fragmentation at a cost to +file-creation speed. + +Defaults to @samp{fast}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean watch-dir-enabled? +If @code{#t}, the directory specified by @code{watch-dir} will be +watched for new @file{.torrent} files and the torrents they describe +added automatically (and the original files removed, if +@code{trash-original-torrent-files?} is @code{#t}). + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-string watch-dir +The directory to be watched for @file{.torrent} files indicating new +torrents to be added, when @code{watch-dir-enabled} is @code{#t}. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean trash-original-torrent-files? +When @code{#t}, @file{.torrent} files will be deleted from the watch +directory once their torrent has been added (see +@code{watch-directory-enabled?}). + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean speed-limit-down-enabled? +When @code{#t}, the daemon's download speed will be limited to the rate +specified by @code{speed-limit-down}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer speed-limit-down +The default global-maximum download speed, in kilobytes per second. + +Defaults to @samp{100}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean speed-limit-up-enabled? +When @code{#t}, the daemon's upload speed will be limited to the rate +specified by @code{speed-limit-up}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer speed-limit-up +The default global-maximum upload speed, in kilobytes per second. + +Defaults to @samp{100}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean alt-speed-enabled? +When @code{#t}, the alternate speed limits @code{alt-speed-down} and +@code{alt-speed-up} are used (in place of @code{speed-limit-down} and +@code{speed-limit-up}, if they are enabled) to constrain the daemon's +bandwidth usage. This can be scheduled to occur automatically at +certain times during the week; see @code{alt-speed-time-enabled?}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer alt-speed-down +The alternate global-maximum download speed, in kilobytes per second. + +Defaults to @samp{50}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer alt-speed-up +The alternate global-maximum upload speed, in kilobytes per second. + +Defaults to @samp{50}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean alt-speed-time-enabled? +When @code{#t}, the alternate speed limits @code{alt-speed-down} and +@code{alt-speed-up} will be enabled automatically during the periods +specified by @code{alt-speed-time-day}, @code{alt-speed-time-begin} and +@code{alt-time-speed-end}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} day-list alt-speed-time-day +The days of the week on which the alternate-speed schedule should be +used, specified either as a list of days (@code{sunday}, @code{monday}, +and so on) or using one of the symbols @code{weekdays}, @code{weekends} +or @code{all}. + +Defaults to @samp{all}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer alt-speed-time-begin +The time of day at which to enable the alternate speed limits, expressed +as a number of minutes since midnight. + +Defaults to @samp{540}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer alt-speed-time-end +The time of day at which to disable the alternate speed limits, +expressed as a number of minutes since midnight. + +Defaults to @samp{1020}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string bind-address-ipv4 +The IP address at which to listen for peer connections, or ``0.0.0.0'' +to listen at all available IP addresses. + +Defaults to @samp{"0.0.0.0"}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string bind-address-ipv6 +The IPv6 address at which to listen for peer connections, or ``::'' to +listen at all available IPv6 addresses. + +Defaults to @samp{"::"}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean peer-port-random-on-start? +If @code{#t}, when the daemon starts it will select a port at random on +which to listen for peer connections, from the range specified +(inclusively) by @code{peer-port-random-low} and +@code{peer-port-random-high}. Otherwise, it listens on the port +specified by @code{peer-port}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} port-number peer-port-random-low +The lowest selectable port number when @code{peer-port-random-on-start?} +is @code{#t}. + +Defaults to @samp{49152}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} port-number peer-port-random-high +The highest selectable port number when @code{peer-port-random-on-start} +is @code{#t}. + +Defaults to @samp{65535}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} port-number peer-port +The port on which to listen for peer connections when +@code{peer-port-random-on-start?} is @code{#f}. + +Defaults to @samp{51413}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean port-forwarding-enabled? +If @code{#t}, the daemon will attempt to configure port-forwarding on an +upstream gateway automatically using @acronym{UPnP} and +@acronym{NAT-PMP}. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} encryption-mode encryption +The encryption preference for peer connections, one of +@code{prefer-unencrypted-connections}, +@code{prefer-encrypted-connections} or +@code{require-encrypted-connections}. + +Defaults to @samp{prefer-encrypted-connections}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-string peer-congestion-algorithm +The TCP congestion-control algorithm to use for peer connections, +specified using a string recognized by the operating system in calls to +@code{setsockopt} (or set to @code{disabled}, in which case the +operating-system default is used). + +Note that on GNU/Linux systems, the kernel must be configured to allow +processes to use a congestion-control algorithm not in the default set; +otherwise, it will deny these requests with ``Operation not permitted''. +To see which algorithms are available on your system and which are +currently permitted for use, look at the contents of the files +@file{tcp_available_congestion_control} and +@file{tcp_allowed_congestion_control} in the @file{/proc/sys/net/ipv4} +directory. + +As an example, to have Transmission Daemon use +@uref{http://www-ece.rice.edu/networks/TCP-LP/,the TCP Low Priority +congestion-control algorithm}, you'll need to modify your kernel +configuration to build in support for the algorithm, then update your +operating-system configuration to allow its use by adding a +@code{sysctl-service-type} service (or updating the existing one's +configuration) with lines like the following: + +@lisp +(service sysctl-service-type + (sysctl-configuration + (settings + ("net.ipv4.tcp_allowed_congestion_control" . + "reno cubic lp")))) +@end lisp + +The Transmission Daemon configuration can then be updated with + +@lisp +(peer-congestion-algorithm "lp") +@end lisp + +and the system reconfigured to have the changes take effect. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} tcp-type-of-service peer-socket-tos +The type of service to request in outgoing @acronym{TCP} packets, one of +@code{default}, @code{low-cost}, @code{throughput}, @code{low-delay} and +@code{reliability}. + +Defaults to @samp{default}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer peer-limit-global +The global limit on the number of connected peers. + +Defaults to @samp{200}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer peer-limit-per-torrent +The per-torrent limit on the number of connected peers. + +Defaults to @samp{50}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer upload-slots-per-torrent +The maximum number of peers to which the daemon will upload data +simultaneously for each torrent. + +Defaults to @samp{14}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer peer-id-ttl-hours +The maximum lifespan, in hours, of the peer ID associated with each +public torrent before it is regenerated. + +Defaults to @samp{6}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean blocklist-enabled? +When @code{#t}, the daemon will ignore peers mentioned in the blocklist +it has most recently downloaded from @code{blocklist-url}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-string blocklist-url +The URL of a peer blocklist (in @acronym{P2P}-plaintext or eMule +@file{.dat} format) to be periodically downloaded and applied when +@code{blocklist-enabled?} is @code{#t}. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean download-queue-enabled? +If @code{#t}, the daemon will be limited to downloading at most +@code{download-queue-size} non-stalled torrents simultaneously. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer download-queue-size +The size of the daemon's download queue, which limits the number of +non-stalled torrents it will download at any one time when +@code{download-queue-enabled?} is @code{#t}. + +Defaults to @samp{5}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean seed-queue-enabled? +If @code{#t}, the daemon will be limited to seeding at most +@code{seed-queue-size} non-stalled torrents simultaneously. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer seed-queue-size +The size of the daemon's seed queue, which limits the number of +non-stalled torrents it will seed at any one time when +@code{seed-queue-enabled?} is @code{#t}. + +Defaults to @samp{10}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean queue-stalled-enabled? +When @code{#t}, the daemon will consider torrents for which it has not +shared data in the past @code{queue-stalled-minutes} minutes to be +stalled and not count them against its @code{download-queue-size} and +@code{seed-queue-size} limits. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer queue-stalled-minutes +The maximum period, in minutes, a torrent may be idle before it is +considered to be stalled, when @code{queue-stalled-enabled?} is +@code{#t}. + +Defaults to @samp{30}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean ratio-limit-enabled? +When @code{#t}, a torrent being seeded will automatically be paused once +it reaches the ratio specified by @code{ratio-limit}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-rational ratio-limit +The ratio at which a torrent being seeded will be paused, when +@code{ratio-limit-enabled?} is @code{#t}. + +Defaults to @samp{2.0}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean idle-seeding-limit-enabled? +When @code{#t}, a torrent being seeded will automatically be paused once +it has been idle for @code{idle-seeding-limit} minutes. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer idle-seeding-limit +The maximum period, in minutes, a torrent being seeded may be idle +before it is paused, when @code{idle-seeding-limit-enabled?} is +@code{#t}. + +Defaults to @samp{30}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean dht-enabled? +Enable @uref{http://bittorrent.org/beps/bep_0005.html,the distributed +hash table (@acronym{DHT}) protocol}, which supports the use of +trackerless torrents. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean lpd-enabled? +Enable @uref{https://en.wikipedia.org/wiki/Local_Peer_Discovery,local +peer discovery} (@acronym{LPD}), which allows the discovery of peers on +the local network and may reduce the amount of data sent over the public +Internet. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean pex-enabled? +Enable @uref{https://en.wikipedia.org/wiki/Peer_exchange,peer exchange} +(@acronym{PEX}), which reduces the daemon's reliance on external +trackers and may improve its performance. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean utp-enabled? +Enable @uref{http://bittorrent.org/beps/bep_0029.html,the micro +transport protocol} (@acronym{uTP}), which aims to reduce the impact of +BitTorrent traffic on other users of the local network while maintaining +full utilization of the available bandwidth. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean rpc-enabled? +If @code{#t}, enable the remote procedure call (@acronym{RPC}) +interface, which allows remote control of the daemon via its Web +interface, the @command{transmission-remote} command-line client, and +similar tools. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string rpc-bind-address +The IP address at which to listen for @acronym{RPC} connections, or +``0.0.0.0'' to listen at all available IP addresses. + +Defaults to @samp{"0.0.0.0"}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} port-number rpc-port +The port on which to listen for @acronym{RPC} connections. + +Defaults to @samp{9091}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string rpc-url +The path prefix to use in the @acronym{RPC}-endpoint @acronym{URL}. + +Defaults to @samp{"/transmission/"}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean rpc-authentication-required? +When @code{#t}, clients must authenticate (see @code{rpc-username} and +@code{rpc-password}) when using the @acronym{RPC} interface. Note this +has the side effect of disabling host-name whitelisting (see +@code{rpc-host-whitelist-enabled?}. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-string rpc-username +The username required by clients to access the @acronym{RPC} interface +when @code{rpc-authentication-required?} is @code{#t}. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-transmission-password-hash rpc-password +The password required by clients to access the @acronym{RPC} interface +when @code{rpc-authentication-required?} is @code{#t}. This must be +specified using a password hash in the format recognized by Transmission +clients, either copied from an existing @file{settings.json} file or +generated using the @code{transmission-password-hash} procedure. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean rpc-whitelist-enabled? +When @code{#t}, @acronym{RPC} requests will be accepted only when they +originate from an address specified in @code{rpc-whitelist}. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string-list rpc-whitelist +The list of IP and IPv6 addresses from which @acronym{RPC} requests will +be accepted when @code{rpc-whitelist-enabled?} is @code{#t}. Wildcards +may be specified using @samp{*}. + +Defaults to @samp{("127.0.0.1" "::1")}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean rpc-host-whitelist-enabled? +When @code{#t}, @acronym{RPC} requests will be accepted only when they +are addressed to a host named in @code{rpc-host-whitelist}. Note that +requests to ``localhost'' or ``localhost.'', or to a numeric address, +are always accepted regardless of these settings. + +Note also this functionality is disabled when +@code{rpc-authentication-required?} is @code{#t}. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} string-list rpc-host-whitelist +The list of host names recognized by the @acronym{RPC} server when +@code{rpc-host-whitelist-enabled?} is @code{#t}. + +Defaults to @samp{()}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} message-level message-level +The minimum severity level of messages to be logged (to +@file{/var/log/transmission.log}) by the daemon, one of @code{none} (no +logging), @code{error}, @code{info} and @code{debug}. + +Defaults to @samp{info}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean start-added-torrents? +When @code{#t}, torrents are started as soon as they are added; +otherwise, they are added in ``paused'' state. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean script-torrent-done-enabled? +When @code{#t}, the script specified by +@code{script-torrent-done-filename} will be invoked each time a torrent +completes. + +Defaults to @samp{#f}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} maybe-file-object script-torrent-done-filename +A file name or file-like object specifying a script to run each time a +torrent completes, when @code{script-torrent-done-enabled?} is +@code{#t}. + +Defaults to @samp{disabled}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean scrape-paused-torrents-enabled? +When @code{#t}, the daemon will scrape trackers for a torrent even when +the torrent is paused. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} non-negative-integer cache-size-mb +The amount of memory, in megabytes, to allocate for the daemon's +in-memory cache. A larger value may increase performance by reducing +the frequency of disk I/O. + +Defaults to @samp{4}. + +@end deftypevr + +@deftypevr {@code{transmission-daemon-configuration} parameter} boolean prefetch-enabled? +When @code{#t}, the daemon will try to improve I/O performance by +hinting to the operating system which data is likely to be read next +from disk to satisfy requests from peers. + +Defaults to @samp{#t}. + +@end deftypevr + + +@c %end of fragment + + + @node Monitoring Services @subsection Monitoring Services diff --git a/gnu/local.mk b/gnu/local.mk index d098c04308..0625c6c5eb 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -605,6 +605,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/dns.scm \ %D%/services/docker.scm \ %D%/services/authentication.scm \ + %D%/services/file-sharing.scm \ %D%/services/games.scm \ %D%/services/ganeti.scm \ %D%/services/getmail.scm \ diff --git a/gnu/services/file-sharing.scm b/gnu/services/file-sharing.scm new file mode 100644 index 0000000000..72cd6478d6 --- /dev/null +++ b/gnu/services/file-sharing.scm @@ -0,0 +1,804 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2020 Simon South <simon@simonsouth.net> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (gnu services file-sharing) + #:use-module (gcrypt base16) + #:use-module (gcrypt hash) + #:use-module (gcrypt random) + #:use-module (gnu services) + #:use-module (gnu services admin) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (gnu packages admin) + #:use-module (gnu packages bittorrent) + #:use-module (gnu packages gnupg) + #:use-module (gnu packages guile) + #:use-module (gnu system shadow) + #:use-module (guix diagnostics) + #:use-module (guix gexp) + #:use-module (guix i18n) + #:use-module (guix modules) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:use-module (rnrs bytevectors) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-34) + #:use-module (srfi srfi-35) + #:export (transmission-daemon-configuration + transmission-daemon-service-type + transmission-password-hash + transmission-random-salt)) + +;;; +;;; Transmission Daemon. +;;; + +(define %transmission-daemon-user "transmission") +(define %transmission-daemon-group "transmission") + +(define %transmission-daemon-configuration-directory + "/var/lib/transmission-daemon") +(define %transmission-daemon-log-file + "/var/log/transmission.log") + +(define %transmission-salt-length 8) + +(define (transmission-password-hash password salt) + "Returns a string containing the result of hashing @var{password} together +with @var{salt}, in the format recognized by Transmission clients for their +@code{rpc-password} configuration setting. + +@var{salt} must be an eight-character string. The +@code{transmission-random-salt} procedure can be used to generate a suitable +salt value at random." + (if (not (and (string? salt) + (eq? (string-length salt) %transmission-salt-length))) + (raise (formatted-message + (G_ "salt value must be a string of ~d characters") + %transmission-salt-length)) + (string-append "{" + (bytevector->base16-string + (sha1 (string->utf8 (string-append password salt)))) + salt))) + +(define (transmission-random-salt) + "Returns a string containing a random, eight-character salt value of the +type generated and used by Transmission clients, suitable for passing to the +@code{transmission-password-hash} procedure." + ;; This implementation matches a portion of Transmission's tr_ssha1 + ;; function. See libtransmission/crypto-utils.c in the Transmission source + ;; distribution. + (let ((salter (string-append "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "./"))) + (list->string + (map (lambda (u8) + (string-ref salter (modulo u8 (string-length salter)))) + (bytevector->u8-list + (gen-random-bv %transmission-salt-length %gcry-strong-random)))))) + +(define (uglify-field-name field-name) + (string-delete #\? (symbol->string field-name))) + +(define (serialize-field field-name val) + ;; "Serialize" each configuration field as a G-expression containing a + ;; name-value pair, the collection of which will subsequently be serialized + ;; to disk as a JSON object. + #~(#$(uglify-field-name field-name) . #$val)) + +(define serialize-boolean serialize-field) +(define serialize-integer serialize-field) +(define serialize-rational serialize-field) + +(define serialize-string serialize-field) +(define-maybe string) +;; Override the definition of "serialize-maybe-string", as we need to output a +;; name-value pair for the JSON builder. +(set! serialize-maybe-string + (lambda (field-name val) + (serialize-string field-name + (if (and (symbol? val) + (eq? val 'disabled)) + "" + val)))) + +(define (string-list? val) + (and (list? val) + (and-map (lambda (x) + (and (string? x) + (not (string-index x #\,)))) + val))) +(define (serialize-string-list field-name val) + (serialize-field field-name (string-join val ","))) + +(define days + '((sunday . #b0000001) + (monday . #b0000010) + (tuesday . #b0000100) + (wednesday . #b0001000) + (thursday . #b0010000) + (friday . #b0100000) + (saturday . #b1000000))) +(define day-lists + (list (cons 'weekdays '(monday tuesday wednesday thursday friday)) + (cons 'weekends '(saturday sunday)) + (cons 'all (map car days)))) +(define (day-list? val) + (or (and (symbol? val) + (assq val day-lists)) + (and (list? val) + (and-map (lambda (x) + (and (symbol? x) + (assq x days))) + val)))) +(define (serialize-day-list field-name val) + (serialize-integer field-name + (reduce logior + #b0000000 + (map (lambda (day) + (assq-ref days day)) + (if (symbol? val) + (assq-ref day-lists val) + val))))) + +(define encryption-modes + '((prefer-unencrypted-connections . 0) + (prefer-encrypted-connections . 1) + (require-encrypted-connections . 2))) +(define (encryption-mode? val) + (and (symbol? val) + (assq val encryption-modes))) +(define (serialize-encryption-mode field-name val) + (serialize-integer field-name (assq-ref encryption-modes val))) + +(define serialize-file-like serialize-field) + +(define (file-object? val) + (or (string? val) + (file-like? val))) +(define (serialize-file-object field-name val) + (if (file-like? val) + (serialize-file-like field-name val) + (serialize-string field-name val))) +(define-maybe file-object) +(set! serialize-maybe-file-object + (lambda (field-name val) + (if (and (symbol? val) + (eq? val 'disabled)) + (serialize-string field-name "") + (serialize-file-object field-name val)))) + +(define (file-object-list? val) + (and (list? val) + (and-map file-object? val))) +(define serialize-file-object-list serialize-field) + +(define message-levels + '((none . 0) + (error . 1) + (info . 2) + (debug . 3))) +(define (message-level? val) + (and (symbol? val) + (assq val message-levels))) +(define (serialize-message-level field-name val) + (serialize-integer field-name (assq-ref message-levels val))) + +(define (non-negative-integer? val) + (and (integer? val) + (not (negative? val)))) +(define serialize-non-negative-integer serialize-integer) + +(define (non-negative-rational? val) + (and (rational? val) + (not (negative? val)))) +(define serialize-non-negative-rational serialize-rational) + +(define (port-number? val) + (and (integer? val) + (>= val 1) + (<= val 65535))) +(define serialize-port-number serialize-integer) + +(define preallocation-modes + '((none . 0) + (fast . 1) + (sparse . 1) + (full . 2))) +(define (preallocation-mode? val) + (and (symbol? val) + (assq val preallocation-modes))) +(define (serialize-preallocation-mode field-name val) + (serialize-integer field-name (assq-ref preallocation-modes val))) + +(define tcp-types-of-service + '((default . "default") + (low-cost . "lowcost") + (throughput . "throughput") + (low-delay . "lowdelay") + (reliability . "reliability"))) +(define (tcp-type-of-service? val) + (and (symbol? val) + (assq val tcp-types-of-service))) +(define (serialize-tcp-type-of-service field-name val) + (serialize-string field-name (assq-ref tcp-types-of-service val))) + +(define (transmission-password-hash? val) + (and (string? val) + (= (string-length val) 49) + (eqv? (string-ref val 0) #\{) + (string-every char-set:hex-digit val 1 41))) +(define serialize-transmission-password-hash serialize-string) +(define-maybe transmission-password-hash) +(set! serialize-maybe-transmission-password-hash serialize-maybe-string) + +(define (umask? val) + (and (integer? val) + (>= val #o000) + (<= val #o777))) +(define serialize-umask serialize-integer) ; must use decimal representation + +(define-configuration transmission-daemon-configuration + ;; Settings internal to this service definition. + (transmission + (package transmission) + "The Transmission package to use.") + (stop-wait-period + (non-negative-integer 10) + "The period, in seconds, to wait when stopping the service for +@command{transmission-daemon} to exit before killing its process. This allows +the daemon time to complete its housekeeping and send a final update to +trackers as it shuts down. On slow hosts, or hosts with a slow network +connection, this value may need to be increased.") + + ;; Files and directories. + (download-dir + (string (string-append %transmission-daemon-configuration-directory + "/downloads")) + "The directory to which torrent files are downloaded.") + (incomplete-dir-enabled? + (boolean #f) + "If @code{#t}, files will be held in @code{incomplete-dir} while their +torrent is being downloaded, then moved to @code{download-dir} once the +torrent is complete. Otherwise, files for all torrents (including those still +being downloaded) will be placed in @code{download-dir}.") + (incomplete-dir + (maybe-string 'disabled) + "The directory in which files from incompletely downloaded torrents will be +held when @code{incomplete-dir-enabled?} is @code{#t}.") + (umask + (umask #o022) + "The file mode creation mask used for downloaded files. (See the +@command{umask} man page for more information.)") + (rename-partial-files? + (boolean #t) + "When @code{#t}, ``.part'' is appended to the name of partially downloaded +files.") + (preallocation + (preallocation-mode 'fast) + "The mode by which space should be preallocated for downloaded files, one +of @code{none}, @code{fast} (or @code{sparse}) and @code{full}. Specifying +@code{full} will minimize disk fragmentation at a cost to file-creation +speed.") + (watch-dir-enabled? + (boolean #f) + "If @code{#t}, the directory specified by @code{watch-dir} will be watched +for new @file{.torrent} files and the torrents they describe added +automatically (and the original files removed, if +@code{trash-original-torrent-files?} is @code{#t}).") + (watch-dir + (maybe-string 'disabled) + "The directory to be watched for @file{.torrent} files indicating new +torrents to be added, when @code{watch-dir-enabled} is @code{#t}.") + (trash-original-torrent-files? + (boolean #f) + "When @code{#t}, @file{.torrent} files will be deleted from the watch +directory once their torrent has been added (see +@code{watch-directory-enabled?}).") + + ;; Bandwidth limits. + (speed-limit-down-enabled? + (boolean #f) + "When @code{#t}, the daemon's download speed will be limited to the rate +specified by @code{speed-limit-down}.") + (speed-limit-down + (non-negative-integer 100) + "The default global-maximum download speed, in kilobytes per second.") + (speed-limit-up-enabled? + (boolean #f) + "When @code{#t}, the daemon's upload speed will be limited to the rate +specified by @code{speed-limit-up}.") + (speed-limit-up + (non-negative-integer 100) + "The default global-maximum upload speed, in kilobytes per second.") + (alt-speed-enabled? + (boolean #f) + "When @code{#t}, the alternate speed limits @code{alt-speed-down} and +@code{alt-speed-up} are used (in place of @code{speed-limit-down} and +@code{speed-limit-up}, if they are enabled) to constrain the daemon's +bandwidth usage. This can be scheduled to occur automatically at certain +times during the week; see @code{alt-speed-time-enabled?}.") + (alt-speed-down + (non-negative-integer 50) + "The alternate global-maximum download speed, in kilobytes per second.") + (alt-speed-up + (non-negative-integer 50) + "The alternate global-maximum upload speed, in kilobytes per second.") + + ;; Bandwidth-limit scheduling. + (alt-speed-time-enabled? + (boolean #f) + "When @code{#t}, the alternate speed limits @code{alt-speed-down} and +@code{alt-speed-up} will be enabled automatically during the periods specified +by @code{alt-speed-time-day}, @code{alt-speed-time-begin} and +@code{alt-time-speed-end}.") + (alt-speed-time-day + (day-list 'all) + "The days of the week on which the alternate-speed schedule should be used, +specified either as a list of days (@code{sunday}, @code{monday}, and so on) +or using one of the symbols @code{weekdays}, @code{weekends} or @code{all}.") + (alt-speed-time-begin + (non-negative-integer 540) + "The time of day at which to enable the alternate speed limits, +expressed as a number of minutes since midnight.") + (alt-speed-time-end + (non-negative-integer 1020) + "The time of day at which to disable the alternate speed limits, +expressed as a number of minutes since midnight.") + + ;; Peer networking. + (bind-address-ipv4 + (string "0.0.0.0") + "The IP address at which to listen for peer connections, or ``0.0.0.0'' to +listen at all available IP addresses.") + (bind-address-ipv6 + (string "::") + "The IPv6 address at which to listen for peer connections, or ``::'' to +listen at all available IPv6 addresses.") + (peer-port-random-on-start? + (boolean #f) + "If @code{#t}, when the daemon starts it will select a port at random on +which to listen for peer connections, from the range specified (inclusively) +by @code{peer-port-random-low} and @code{peer-port-random-high}. Otherwise, +it listens on the port specified by @code{peer-port}.") + (peer-port-random-low + (port-number 49152) + "The lowest selectable port number when @code{peer-port-random-on-start?} +is @code{#t}.") + (peer-port-random-high + (port-number 65535) + "The highest selectable port number when @code{peer-port-random-on-start} +is @code{#t}.") + (peer-port + (port-number 51413) + "The port on which to listen for peer connections when +@code{peer-port-random-on-start?} is @code{#f}.") + (port-forwarding-enabled? + (boolean #t) + "If @code{#t}, the daemon will attempt to configure port-forwarding on an +upstream gateway automatically using @acronym{UPnP} and @acronym{NAT-PMP}.") + (encryption + (encryption-mode 'prefer-encrypted-connections) + "The encryption preference for peer connections, one of +@code{prefer-unencrypted-connections}, @code{prefer-encrypted-connections} or +@code{require-encrypted-connections}.") + (peer-congestion-algorithm + (maybe-string 'disabled) + "The TCP congestion-control algorithm to use for peer connections, +specified using a string recognized by the operating system in calls to +@code{setsockopt} (or set to @code{disabled}, in which case the +operating-system default is used). + +Note that on GNU/Linux systems, the kernel must be configured to allow +processes to use a congestion-control algorithm not in the default set; +otherwise, it will deny these requests with ``Operation not permitted''. To +see which algorithms are available on your system and which are currently +permitted for use, look at the contents of the files +@file{tcp_available_congestion_control} and +@file{tcp_allowed_congestion_control} in the @file{/proc/sys/net/ipv4} +directory. + +As an example, to have Transmission Daemon use +@uref{http://www-ece.rice.edu/networks/TCP-LP/, the TCP Low Priority +congestion-control algorithm}, you'll need to modify your kernel configuration +to build in support for the algorithm, then update your operating-system +configuration to allow its use by adding a @code{sysctl-service-type} +service (or updating the existing one's configuration) with lines like the +following: + +@lisp +(service sysctl-service-type + (sysctl-configuration + (settings + (\"net.ipv4.tcp_allowed_congestion_control\" . + \"reno cubic lp\")))) +@end lisp + +The Transmission Daemon configuration can then be updated with + +@lisp +(peer-congestion-algorithm \"lp\") +@end lisp + +and the system reconfigured to have the changes take effect.") + (peer-socket-tos + (tcp-type-of-service 'default) + "The type of service to request in outgoing @acronym{TCP} packets, +one of @code{default}, @code{low-cost}, @code{throughput}, @code{low-delay} +and @code{reliability}.") + (peer-limit-global + (non-negative-integer 200) + "The global limit on the number of connected peers.") + (peer-limit-per-torrent + (non-negative-integer 50) + "The per-torrent limit on the number of connected peers.") + (upload-slots-per-torrent + (non-negative-integer 14) + "The maximum number of peers to which the daemon will upload data +simultaneously for each torrent.") + (peer-id-ttl-hours + (non-negative-integer 6) + "The maximum lifespan, in hours, of the peer ID associated with each public +torrent before it is regenerated.") + + ;; Peer blocklists. + (blocklist-enabled? + (boolean #f) + "When @code{#t}, the daemon will ignore peers mentioned in the blocklist it +has most recently downloaded from @code{blocklist-url}.") + (blocklist-url + (maybe-string 'disabled) + "The URL of a peer blocklist (in @acronym{P2P}-plaintext or eMule +@file{.dat} format) to be periodically downloaded and applied when +@code{blocklist-enabled?} is @code{#t}.") + + ;; Queueing. + (download-queue-enabled? + (boolean #t) + "If @code{#t}, the daemon will be limited to downloading at most +@code{download-queue-size} non-stalled torrents simultaneously.") + (download-queue-size + (non-negative-integer 5) + "The size of the daemon's download queue, which limits the number of +non-stalled torrents it will download at any one time when +@code{download-queue-enabled?} is @code{#t}.") + (seed-queue-enabled? + (boolean #f) + "If @code{#t}, the daemon will be limited to seeding at most +@code{seed-queue-size} non-stalled torrents simultaneously.") + (seed-queue-size + (non-negative-integer 10) + "The size of the daemon's seed queue, which limits the number of +non-stalled torrents it will seed at any one time when +@code{seed-queue-enabled?} is @code{#t}.") + (queue-stalled-enabled? + (boolean #t) + "When @code{#t}, the daemon will consider torrents for which it has not +shared data in the past @code{queue-stalled-minutes} minutes to be stalled and +not count them against its @code{download-queue-size} and +@code{seed-queue-size} limits.") + (queue-stalled-minutes + (non-negative-integer 30) + "The maximum period, in minutes, a torrent may be idle before it is +considered to be stalled, when @code{queue-stalled-enabled?} is @code{#t}.") + + ;; Seeding limits. + (ratio-limit-enabled? + (boolean #f) + "When @code{#t}, a torrent being seeded will automatically be paused once +it reaches the ratio specified by @code{ratio-limit}.") + (ratio-limit + (non-negative-rational 2.0) + "The ratio at which a torrent being seeded will be paused, when +@code{ratio-limit-enabled?} is @code{#t}.") + (idle-seeding-limit-enabled? + (boolean #f) + "When @code{#t}, a torrent being seeded will automatically be paused once +it has been idle for @code{idle-seeding-limit} minutes.") + (idle-seeding-limit + (non-negative-integer 30) + "The maximum period, in minutes, a torrent being seeded may be idle before +it is paused, when @code{idle-seeding-limit-enabled?} is @code{#t}.") + + ;; BitTorrent extensions. + (dht-enabled? + (boolean #t) + "Enable @uref{http://bittorrent.org/beps/bep_0005.html, the distributed +hash table (@acronym{DHT}) protocol}, which supports the use of trackerless +torrents.") + (lpd-enabled? + (boolean #f) + "Enable @url{https://en.wikipedia.org/wiki/Local_Peer_Discovery, local peer +discovery} (@acronym{LPD}), which allows the discovery of peers on the local +network and may reduce the amount of data sent over the public Internet.") + (pex-enabled? + (boolean #t) + "Enable @url{https://en.wikipedia.org/wiki/Peer_exchange, peer +exchange} (@acronym{PEX}), which reduces the daemon's reliance on external +trackers and may improve its performance.") + (utp-enabled? + (boolean #t) + "Enable @url{http://bittorrent.org/beps/bep_0029.html, the micro transport +protocol} (@acronym{uTP}), which aims to reduce the impact of BitTorrent +traffic on other users of the local network while maintaining full utilization +of the available bandwidth.") + + ;; Remote procedure call (RPC) interface. + (rpc-enabled? + (boolean #t) + "If @code{#t}, enable the remote procedure call (@acronym{RPC}) interface, +which allows remote control of the daemon via its Web interface, the +@command{transmission-remote} command-line client, and similar tools.") + (rpc-bind-address + (string "0.0.0.0") + "The IP address at which to listen for @acronym{RPC} connections, or +``0.0.0.0'' to listen at all available IP addresses.") + (rpc-port + (port-number 9091) + "The port on which to listen for @acronym{RPC} connections.") + (rpc-url + (string "/transmission/") + "The path prefix to use in the @acronym{RPC}-endpoint @acronym{URL}.") + (rpc-authentication-required? + (boolean #f) + "When @code{#t}, clients must authenticate (see @code{rpc-username} and +@code{rpc-password}) when using the @acronym{RPC} interface. Note this has +the side effect of disabling host-name whitelisting (see +@code{rpc-host-whitelist-enabled?}.") + (rpc-username + (maybe-string 'disabled) + "The username required by clients to access the @acronym{RPC} interface +when @code{rpc-authentication-required?} is @code{#t}.") + (rpc-password + (maybe-transmission-password-hash 'disabled) + "The password required by clients to access the @acronym{RPC} interface +when @code{rpc-authentication-required?} is @code{#t}. This must be specified +using a password hash in the format recognized by Transmission clients, either +copied from an existing @file{settings.json} file or generated using the +@code{transmission-password-hash} procedure.") + (rpc-whitelist-enabled? + (boolean #t) + "When @code{#t}, @acronym{RPC} requests will be accepted only when they +originate from an address specified in @code{rpc-whitelist}.") + (rpc-whitelist + (string-list '("127.0.0.1" "::1")) + "The list of IP and IPv6 addresses from which @acronym{RPC} requests will +be accepted when @code{rpc-whitelist-enabled?} is @code{#t}. Wildcards may be +specified using @samp{*}.") + (rpc-host-whitelist-enabled? + (boolean #t) + "When @code{#t}, @acronym{RPC} requests will be accepted only when they are +addressed to a host named in @code{rpc-host-whitelist}. Note that requests to +``localhost'' or ``localhost.'', or to a numeric address, are always accepted +regardless of these settings. + +Note also this functionality is disabled when +@code{rpc-authentication-required?} is @code{#t}.") + (rpc-host-whitelist + (string-list '()) + "The list of host names recognized by the @acronym{RPC} server when +@code{rpc-host-whitelist-enabled?} is @code{#t}.") + + ;; Miscellaneous. + (message-level + (message-level 'info) + "The minimum severity level of messages to be logged (to +@file{/var/log/transmission.log}) by the daemon, one of @code{none} (no +logging), @code{error}, @code{info} and @code{debug}.") + (start-added-torrents? + (boolean #t) + "When @code{#t}, torrents are started as soon as they are added; otherwise, +they are added in ``paused'' state.") + (script-torrent-done-enabled? + (boolean #f) + "When @code{#t}, the script specified by +@code{script-torrent-done-filename} will be invoked each time a torrent +completes.") + (script-torrent-done-filename + (maybe-file-object 'disabled) + "A file name or file-like object specifying a script to run each time a +torrent completes, when @code{script-torrent-done-enabled?} is @code{#t}.") + (scrape-paused-torrents-enabled? + (boolean #t) + "When @code{#t}, the daemon will scrape trackers for a torrent even when +the torrent is paused.") + (cache-size-mb + (non-negative-integer 4) + "The amount of memory, in megabytes, to allocate for the daemon's in-memory +cache. A larger value may increase performance by reducing the frequency of +disk I/O.") + (prefetch-enabled? + (boolean #t) + "When @code{#t}, the daemon will try to improve I/O performance by hinting +to the operating system which data is likely to be read next from disk to +satisfy requests from peers.")) + +(define (transmission-daemon-shepherd-service config) + "Return a <shepherd-service> for Transmission Daemon with CONFIG." + (let ((transmission + (transmission-daemon-configuration-transmission config)) + (stop-wait-period + (transmission-daemon-configuration-stop-wait-period config))) + (list + (shepherd-service + (provision '(transmission-daemon transmission bittorrent)) + (requirement '(networking)) + (documentation "Share files using the BitTorrent protocol.") + (start #~(make-forkexec-constructor + '(#$(file-append transmission "/bin/transmission-daemon") + "--config-dir" + #$%transmission-daemon-configuration-directory + "--foreground") + #:user #$%transmission-daemon-user + #:group #$%transmission-daemon-group + #:directory #$%transmission-daemon-configuration-directory + #:log-file #$%transmission-daemon-log-file + #:environment-variables + '("CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt"))) + (stop #~(lambda (pid) + (kill pid SIGTERM) + + ;; Transmission Daemon normally needs some time to shut down, + ;; as it will complete some housekeeping and send a final + ;; update to trackers before it exits. + ;; + ;; Wait a reasonable period for it to stop before continuing. + ;; If we don't do this, restarting the service can fail as the + ;; new daemon process finds the old one still running and + ;; attached to the port used for peer connections. + (let wait-before-killing ((period #$stop-wait-period)) + (if (zero? (car (waitpid pid WNOHANG))) + (if (positive? period) + (begin + (sleep 1) + (wait-before-killing (- period 1))) + (begin + (format #t + #$(G_ "Wait period expired; killing \ +transmission-daemon (pid ~a).~%") + pid) + (display #$(G_ "(If you see this message \ +regularly, you may need to increase the value +of 'stop-wait-period' in the service configuration.)\n")) + (kill pid SIGKILL))))) + #f)) + (actions + (list + (shepherd-action + (name 'reload) + (documentation "Reload the settings file from disk.") + (procedure #~(lambda (pid) + (if pid + (begin + (kill pid SIGHUP) + (display #$(G_ "Service transmission-daemon has \ +been asked to reload its settings file."))) + (display #$(G_ "Service transmission-daemon is not \ +running.")))))))))))) + +(define %transmission-daemon-accounts + (list (user-group + (name %transmission-daemon-group) + (system? #t)) + (user-account + (name %transmission-daemon-user) + (group %transmission-daemon-group) + (comment "Transmission Daemon service account") + (home-directory %transmission-daemon-configuration-directory) + (shell (file-append shadow "/sbin/nologin")) + (system? #t)))) + +(define %transmission-daemon-log-rotations + (list (log-rotation + (files (list %transmission-daemon-log-file))))) + +(define (transmission-daemon-computed-settings-file config) + "Return a @code{computed-file} object that, when unquoted in a G-expression, +produces a Transmission settings file (@file{settings.json}) matching CONFIG." + (let ((settings + ;; "Serialize" the configuration settings as a list of G-expressions + ;; containing a name-value pair, which will ultimately be sorted and + ;; serialized to the settings file as a JSON object. + (map + (lambda (field) + ((configuration-field-serializer field) + (configuration-field-name field) + ((configuration-field-getter field) config))) + (filter + (lambda (field) + ;; Omit configuration fields that are used only internally by + ;; this service definition. + (not (memq (configuration-field-name field) + '(transmission stop-wait-period)))) + transmission-daemon-configuration-fields)))) + (computed-file + "settings.json" + (with-extensions (list guile-gcrypt guile-json-4) + (with-imported-modules (source-module-closure '((json builder))) + #~(begin + (use-modules (json builder)) + + (with-output-to-file #$output + (lambda () + (scm->json (sort-list '(#$@settings) + (lambda (x y) + (string<=? (car x) (car y)))) + #:pretty #t))))))))) + +(define (transmission-daemon-activation config) + "Return the Transmission Daemon activation GEXP for CONFIG." + (let ((config-dir %transmission-daemon-configuration-directory) + (incomplete-dir-enabled + (transmission-daemon-configuration-incomplete-dir-enabled? config)) + (incomplete-dir + (transmission-daemon-configuration-incomplete-dir config)) + (watch-dir-enabled + (transmission-daemon-configuration-watch-dir-enabled? config)) + (watch-dir + (transmission-daemon-configuration-watch-dir config))) + (with-imported-modules (source-module-closure '((guix build utils))) + #~(begin + (use-modules (guix build utils)) + + (let ((owner (getpwnam #$%transmission-daemon-user))) + (define (mkdir-p/perms directory perms) + (mkdir-p directory) + (chown directory (passwd:uid owner) (passwd:gid owner)) + (chmod directory perms)) + + ;; Create the directories Transmission Daemon is configured to use + ;; and assign them suitable permissions. + (for-each (lambda (directory-specification) + (apply mkdir-p/perms directory-specification)) + '(#$@(append + `((,config-dir #o750)) + (if incomplete-dir-enabled + `((,incomplete-dir #o750)) + '()) + (if watch-dir-enabled + `((,watch-dir #o770)) + '()))))) + + ;; Generate and activate the daemon's settings file, settings.json. + (activate-special-files + '((#$(string-append config-dir "/settings.json") + #$(transmission-daemon-computed-settings-file config)))))))) + +(define transmission-daemon-service-type + (service-type + (name 'transmission) + (extensions + (list (service-extension shepherd-root-service-type + transmission-daemon-shepherd-service) + (service-extension account-service-type + (const %transmission-daemon-accounts)) + (service-extension rottlog-service-type + (const %transmission-daemon-log-rotations)) + (service-extension activation-service-type + transmission-daemon-activation))) + (default-value (transmission-daemon-configuration)) + (description "Share files using the BitTorrent protocol."))) + +(define (generate-transmission-daemon-documentation) + (generate-documentation + `((transmission-daemon-configuration + ,transmission-daemon-configuration-fields)) + 'transmission-daemon-configuration)) diff --git a/po/packages/POTFILES.in b/po/packages/POTFILES.in index 9a178edfa6..398f9adfdf 100644 --- a/po/packages/POTFILES.in +++ b/po/packages/POTFILES.in @@ -59,5 +59,6 @@ gnu/packages/wordnet.scm gnu/packages/xiph.scm gnu/services/base.scm gnu/services/certbot.scm +gnu/services/file-sharing.scm gnu/services/networking.scm gnu/services/version-control.scm diff --git a/tests/services/file-sharing.scm b/tests/services/file-sharing.scm new file mode 100644 index 0000000000..27bec57325 --- /dev/null +++ b/tests/services/file-sharing.scm @@ -0,0 +1,59 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2020 Simon South <simon@simonsouth.net> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (tests services file-sharing) + #:use-module (gnu services file-sharing) + #:use-module (srfi srfi-64)) + +;;; Tests for the (gnu services file-sharing) module. + +(test-begin "file-sharing") + + +;;; +;;; Transmission Daemon. +;;; + +(define %transmission-salt-length 8) + +(define (valid-transmission-salt? salt) + (and (string? salt) + (eqv? (string-length salt) %transmission-salt-length))) + +(test-assert "transmission-random-salt" + (valid-transmission-salt? (transmission-random-salt))) + +(test-equal "transmission-password-hash, typical values" + "{ef6fba106cdef3aac64d1410090cae353cbecde53ceVVQO2" + (transmission-password-hash "transmission" "3ceVVQO2")) + +(test-equal "transmission-password-hash, empty password" + "{820f816515d8969d058d07a1de018650619ee7ffCp.I5SWg" + (transmission-password-hash "" "Cp.I5SWg")) + +(test-error "transmission-password-hash, salt value too short" + (transmission-password-hash + "transmission" + (make-string (- %transmission-salt-length 1) #\a))) + +(test-error "transmission-password-hash, salt value too long" + (transmission-password-hash + "transmission" + (make-string (+ %transmission-salt-length 1) #\a))) + +(test-end "file-sharing") |