aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2021, 2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;;
;;; 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 rsync)
  #:use-module ((gnu build linux-container) #:select (%namespaces))
  #:use-module (gnu services)
  #:use-module (gnu services base)
  #:use-module (gnu services shepherd)
  #:autoload   (gnu system file-systems) (file-system-mapping)
  #:use-module (gnu system shadow)
  #:use-module (gnu packages admin)
  #:use-module (gnu packages linux)
  #:use-module (gnu packages rsync)
  #:use-module (guix records)
  #:use-module (guix gexp)
  #:use-module (guix diagnostics)
  #:use-module (guix i18n)
  #:use-module (guix least-authority)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (ice-9 match)
  #:export (rsync-configuration
            rsync-configuration?
            rsync-configuration-modules

            rsync-module
            rsync-module?
            rsync-module-name
            rsync-module-file-name
            rsync-module-comment
            rsync-module-read-only
            rsync-module-timeout

            rsync-service-type))

;;;; Commentary:
;;;
;;; This module implements a service that to run instance of Rsync,
;;; files synchronization tool.
;;;
;;;; Code:

(define-with-syntax-properties (warn-share-field-deprecation (value properties))
  (unless (unspecified? value)
    (warning (source-properties->location properties)
             (G_ "the 'share-path' and 'share-comment' fields is deprecated, \
please use 'modules' instead~%")))
  value)

(define-record-type* <rsync-configuration>
  rsync-configuration
  make-rsync-configuration
  rsync-configuration?
  (package       rsync-configuration-package              ; file-like
                 (default rsync))
  (address       rsync-configuration-address              ; string | #f
                 (default #f))
  (port-number   rsync-configuration-port-number          ; integer
                 (default 873))
  (pid-file      rsync-configuration-pid-file             ; string
                 (default "/var/run/rsyncd/rsyncd.pid"))
  (lock-file     rsync-configuration-lock-file            ; string
                 (default "/var/run/rsyncd/rsyncd.lock"))
  (log-file      rsync-configuration-log-file             ; string
                 (default "/var/log/rsyncd.log"))
  (use-chroot?   rsync-configuration-use-chroot?          ; boolean
                 (sanitize warn-share-field-deprecation)
                 (default *unspecified*))
  (modules       rsync-configuration-actual-modules ;list of <rsync-module>
                 (default %default-modules))  ;TODO: eventually remove default
  (share-path    rsync-configuration-share-path           ; string
                 (sanitize warn-share-field-deprecation)
                 (default *unspecified*))
  (share-comment rsync-configuration-share-comment        ; string
                 (sanitize warn-share-field-deprecation)
                 (default *unspecified*))
  (read-only?    rsync-configuration-read-only?           ; boolean
                 (sanitize warn-share-field-deprecation)
                 (default *unspecified*))
  (timeout       rsync-configuration-timeout              ; integer
                 (sanitize warn-share-field-deprecation)
                 (default *unspecified*))
  (user          rsync-configuration-user                 ; string
                 (default "root"))
  (group         rsync-configuration-group                ; string
                 (default "root"))
  (uid           rsync-configuration-uid                  ; string
                 (default "rsyncd"))
  (gid           rsync-configuration-gid                  ; string
                 (default "rsyncd")))

;; Rsync "module": a directory exported the rsync protocol.
(define-record-type* <rsync-module>
  rsync-module make-rsync-module
  rsync-module?
  (name          rsync-module-name)               ;string
  (file-name     rsync-module-file-name)          ;string
  (comment       rsync-module-comment             ;string
                 (default ""))
  (read-only?    rsync-module-read-only?          ;boolean
                 (default #t))
  (chroot?       rsync-module-chroot?             ;boolean
                 (default #t))
  (timeout       rsync-module-timeout             ;integer
                 (default 300)))

(define %default-modules
  ;; Default modules, provided for backward compatibility.
  (list (rsync-module (name "files")
                      (file-name "/srv/rsyncd")
                      (comment "Rsync share")
                      (read-only? #f))))          ;yes, that was the default

(define (rsync-configuration-modules config)
  (match-record config <rsync-configuration>
    (modules
     share-path share-comment use-chroot? read-only? timeout) ;deprecated
    (if (unspecified? share-path)
        (rsync-configuration-actual-modules config)
        (list (rsync-module                       ;backward compatibility
               (name "files")
               (file-name share-path)
               (comment "Rsync share")
               (chroot?
                (if (unspecified? use-chroot?) #t use-chroot?))
               (read-only?
                (if (unspecified? read-only?) #f read-only?))
               (timeout
                (if (unspecified? timeout) 300 timeout)))))))

(define (rsync-account config)
  "Return the user accounts and user groups for CONFIG."
  (let ((rsync-user (if (rsync-configuration-uid config)
                        (rsync-configuration-uid config)
                        (rsync-configuration-user config)))
        (rsync-group (if (rsync-configuration-gid config)
                         (rsync-configuration-gid config)
                         (rsync-configuration-group config))))
    (list (user-group (name rsync-group) (system? #t))
          (user-account
           (name rsync-user)
           (system? #t)
           (group rsync-group)
           (comment "rsyncd privilege separation user")
           (home-directory (string-append "/var/run/"
                                          rsync-user))
           (shell (file-append shadow "/sbin/nologin"))))))

(define (rsync-activation config)
  "Return the activation GEXP for CONFIG."
  (with-imported-modules '((guix build utils))
    #~(begin
        (let ((user  (getpw (if #$(rsync-configuration-uid config)
                                #$(rsync-configuration-uid config)
                                #$(rsync-configuration-user config))))
              (group (getpw (if #$(rsync-configuration-gid config)
                                #$(rsync-configuration-gid config)
                                #$(rsync-configuration-group config)))))
          (mkdir-p (dirname #$(rsync-configuration-pid-file config)))
          (for-each (lambda (directory)
                      (mkdir-p directory)
                      (chown directory (passwd:uid user) (group:gid group)))
                    '#$(map rsync-module-file-name
                            (rsync-configuration-modules config)))))))

(define (rsync-config-file config)
  ;; Return the rsync configuration file corresponding to CONFIG.
  (define (module-config module)
    (match-record module <rsync-module>
      (name file-name comment chroot? read-only? timeout)
      (list "[" name "]\n"
            "  path = " file-name "\n"
            "  use chroot = " (if chroot? "true" "false") "\n"
            "  comment = " comment "\n"
            "  read only = " (if read-only? "true" "false") "\n"
            "  timeout = " (number->string timeout) "\n")))

  (define modules
    (rsync-configuration-modules config))

  (match-record config <rsync-configuration>
    (package address port-number pid-file lock-file log-file
             user group uid gid)
    (unless (string=? user "root")
      (cond
       ((<= port-number 1024)
        (error (string-append "rsync-service: to run on port "
                              (number->string port-number)
                              ", user must be root.")))
       ((find rsync-module-chroot? modules)
        (error (string-append "rsync-service: to run in a chroot"
                              ", user must be root.")))
       (uid
        (error "rsync-service: to use uid, user must be root."))
       (gid
        (error "rsync-service: to use gid, user must be root."))))

    (apply mixed-text-file "rsync.conf"
           "# Generated by 'rsync-service'.\n\n"
           "pid file = " pid-file "\n"
           "lock file = " lock-file "\n"
           "log file = " log-file "\n"
           (if address (string-append "address = " address "\n") "")
           "port = " (number->string port-number) "\n"
           (if uid (string-append "uid = " uid "\n") "")
           "gid = " (if gid gid "nogroup") "\n"   ; no group nobody
           "\n\n"
           (append-map module-config modules))))

(define (rsync-shepherd-service config)
  "Return a <shepherd-service> for rsync with CONFIG."

  ;; XXX: Predicates copied from (gnu services ssh).
  (define inetd-style?
    #~(and (defined? 'make-inetd-constructor)
           (not (string=? (@ (shepherd config) Version) "0.9.0"))))

  (define ipv6-support?
    #~(catch 'system-error
        (lambda ()
          (let ((sock (socket AF_INET6 SOCK_STREAM 0)))
            (close-port sock)
            #t))
        (const #f)))

  (define (module->file-system-mapping module)
    "Return the <file-system-mapping> record corresponding to MODULE, an
<rsync-module> object."
    (match-record module <rsync-module>
      (file-name read-only?)
      (file-system-mapping
       (source file-name)
       (target source)
       (writable? (not read-only?)))))

  (match-record config <rsync-configuration>
    (package log-file modules pid-file port-number user group)
    ;; Run the rsync daemon in its own 'mnt' namespace, to guard against
    ;; change to mount points it may be serving.
    (let* ((config-file (rsync-config-file config))
           (rsync-command #~(list #$(least-authority-wrapper
                                     (file-append rsync "/bin/rsync")
                                     #:name "rsync"
                                     #:namespaces (fold delq %namespaces
                                                        '(net user))
                                     #:mappings
                                     (append (list (file-system-mapping
                                                    (source "/var/run/rsyncd")
                                                    (target source)
                                                    (writable? #t))
                                                   (file-system-mapping
                                                    (source (dirname log-file))
                                                    (target source)
                                                    (writable? #t))
                                                   (file-system-mapping
                                                    (source config-file)
                                                    (target source)))
                                             (map module->file-system-mapping
                                                  modules)))
                                  "--config" #$config-file "--daemon")))
      (list (shepherd-service
             (provision '(rsync))
             (documentation "Run rsync daemon.")
             (actions (list (shepherd-configuration-action config-file)))
             (start #~(if #$inetd-style?
                          (make-inetd-constructor
                           #$rsync-command
                           (cons (endpoint
                                  (make-socket-address AF_INET INADDR_ANY
                                                       #$port-number))
                                 (if #$ipv6-support?
                                     (list
                                      (endpoint
                                       (make-socket-address AF_INET6 IN6ADDR_ANY
                                                            #$port-number)))
                                     '()))
                           #:service-name-stem "rsync"
                           #:user #$user
                           #:group #$group)
                          (make-forkexec-constructor #$rsync-command
                                                     #:pid-file #$pid-file
                                                     #:user #$user
                                                     #:group #$group)))
             (stop #~(if #$inetd-style?
                         (make-inetd-destructor)
                         (make-kill-destructor))))))))

(define rsync-service-type
  (service-type
   (name 'rsync)
   (extensions
    (list (service-extension shepherd-root-service-type rsync-shepherd-service)
          (service-extension account-service-type rsync-account)
          (service-extension activation-service-type rsync-activation)))
   (default-value (rsync-configuration))
   (description
    "Run the rsync file copying tool in daemon mode.  This allows remote hosts
to keep synchronized copies of the files exported by rsync.")))
7f5f7a29c3ce05766071b57e8cdb271'>import: Importers return prefixed licenses....* guix/import/utils.scm (define-module): Import licenses with license: prefix. (string->licenses): Use prefixed licenses. (license->symbol): Return symbols with the prefix license:. * guix/tests/pypi.scm (pypi->guix-package): Update test cases. * guix/tests/gem.scm (gem->guix-package): Update test case. David Craven 2016-07-26import: pypi: Correctly handle new-style URLs....Fixes <http://bugs.gnu.org/23997>. * guix/import/pypi.scm (guix-package->pypi-name): Rewrite using 'basename' and 'hyphen-package-name->name+version'. * tests/pypi.scm ("guix-package->pypi-name, old URL style") ("guix-package->pypi-name, new URL style"): New tests. Ludovic Courtès 2016-06-14import: pypi: read requirements from wheels....* doc/guix.tex (Invoking guix import): Mention that the pypi importer works better with "unzip". * guix/import/pypi.scm (latest-wheel-release, wheel-url->extracted-directory): New procedures. * tests/pypi.scm (("pypi->guix-package, wheels"): New test. Cyril Roelandt 2016-04-03build: Add a Guile custom test driver using SRFI-64....Before that '.log' files for scheme tests were fragmented and not included in test-suite.log. This unifies the semantics of SRFI-64 API with Automake test suite. * build-aux/test-driver.scm: New file. * Makefile.am (SCM_LOG_DRIVER, AM_SCM_LOG_DRIVER_FLAGS): New variables. (SCM_LOG_COMPILER, AM_SCM_LOG_FLAGS): Delete variables. (AM_TESTS_ENVIRONMENT): Set GUILE_AUTO_COMPILE to 0. * test-env.in: Silence guix-daemon. * doc/guix.texi (Running the Test Suite): Describe how to display the detailed results. Bug reports require only 'test-suite.log' file. * tests/base32.scm, tests/build-utils.scm, tests/builders.scm, tests/challenge.scm, tests/cpan.scm, tests/cpio.scm, tests/cran.scm, tests/cve.scm, tests/derivations.scm, tests/elpa.scm, tests/file-systems.scm, tests/gem.scm, tests/gexp.scm, tests/gnu-maintenance.scm, tests/grafts.scm, tests/graph.scm, tests/gremlin.scm, tests/hackage.scm, tests/hash.scm, tests/import-utils.scm, tests/lint.scm, tests/monads.scm, tests/nar.scm, tests/packages.scm, tests/pk-crypto.scm, tests/pki.scm, tests/profiles.scm, tests/publish.scm, tests/pypi.scm, tests/records.scm, tests/scripts-build.scm, tests/scripts.scm, tests/services.scm, tests/sets.scm, tests/size.scm, tests/snix.scm, tests/store.scm, tests/substitute.scm, tests/syscalls.scm, tests/system.scm, tests/ui.scm, tests/union.scm, tests/upstream.scm, tests/utils.scm: Don't exit at the end of test groups. * tests/containers.scm: Likewise. Use 'test-skip' instead of exiting with error code 77. Mathieu Lirzin 2016-03-19import: pypi: Emit 'pypi-uri' only when it yields the right URL....Fixes <http://bugs.gnu.org/23062>. Reported by Danny Milosavljevic <dannym@scratchpost.org>. * guix/import/pypi.scm (make-pypi-sexp): Check whether 'pypi-uri' returns SOURCE-URL and fall back to the full URL otherwise. * tests/pypi.scm ("pypi->guix-package"): Adjust expected URI accordingly. Co-authored-by: Danny Milosavljevic <dannym@scratchpost.org> Ludovic Courtès 2015-11-03import: pypi: Use "pypi-uri" instead of building the URL manually....* guix/import/pypi.scm (make-pypi-sexp): Use "pypi-uri". * tests/pypi.scm: Update the tests accordingly. Cyril Roelandt 2015-06-24import: pypi: Detect inputs....* guix/import/pypi.scm (python->package-name, maybe-inputs, compute-inputs, guess-requirements): New procedures. * guix/import/pypi.scm (guix-hash-url): Now takes a filename instead of an URL as input. * guix/import/pypi.scm (make-pypi-sexp): Now tries to generate the inputs automagically. * tests/pypi.scm: Update the test. Cyril Roelandt 2015-01-09tests: import: Factorize utility function....* tests/pypi.scm (mock): Move this... * guix/tests.scm: to here. Eric Bavier 2015-01-09import: Factorize utility functions....* guix/import/pypi.scm (hash-table->alist, flatten, assoc-ref*, url-fetch, json-fetch): Pull procedures from here into... * guix/import/utils.scm: Here and... * guix/import/json.scm: Here. New file. * Makefile.am (MODULE)[HAVE_GUILE_JSON]: Add it. * guix/import/gnu.scm (file-sha256): Move from here to... * guix/hash.scm: Here. * tests/pypi.scm (pypi->guix-package): Update mock module reference. Eric Bavier 2014-09-29import: Add PyPI importer....* guix/snix.scm: Delete. * guix/import/snix.scm: New file. * guix/import/pypi.scm: New file. * guix/import/utils.scm: New file. * guix/scripts/import/nix.scm: New file. * guix/scripts/import/pypi.scm: New file. * tests/pypi.scm: New file. * tests/snix.scm: Import (guix import snix) module. * guix/scripts/import.scm (%default-options, %options): Delete. (%standard-import-options, importers): New variables. (show-help): List importers. (guix-import): Factor out Nix-specific logic. Delegate to correct importer based upon first argument. * configure.ac (HAVE_GUILE_JSON): New conditional. * Makefile.am (MODULES): Add new files and remove 'guix/snix.scm'. (SCM_TESTS): Add 'tests/pypi.scm' if guile-json is installed. David Thompson