aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2017 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019–2020, 2024 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
;;; 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 system uuid)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-9)
  #:use-module (rnrs bytevectors)
  #:use-module (ice-9 match)
  #:use-module (ice-9 vlist)
  #:use-module (ice-9 regex)
  #:use-module (ice-9 format)
  #:export (uuid
            uuid?
            uuid-type
            uuid-bytevector
            uuid=?

            bytevector->uuid

            uuid->string
            dce-uuid->string
            string->uuid
            string->dce-uuid
            string->iso9660-uuid
            string->ext2-uuid
            string->ext3-uuid
            string->ext4-uuid
            string->bcachefs-uuid
            string->btrfs-uuid
            string->fat-uuid
            string->jfs-uuid
            string->ntfs-uuid
            string->xfs-uuid
            iso9660-uuid->string

            ;; XXX: For lack of a better place.
            sub-bytevector
            latin1->string))


;;;
;;; Tools that lack a better place.
;;;

(define (sub-bytevector bv start size)
  "Return a copy of the SIZE bytes of BV starting from offset START."
  (let ((result (make-bytevector size)))
    (bytevector-copy! bv start result 0 size)
    result))

(define (latin1->string bv terminator)
  "Return a string of BV, a latin1 bytevector, or #f.  TERMINATOR is a predicate
that takes a number and returns #t when a termination character is found."
    (let ((bytes (take-while (negate terminator) (bytevector->u8-list bv))))
      (if (null? bytes)
          #f
          (list->string (map integer->char bytes)))))


;;;
;;; DCE UUIDs.
;;;

(define-syntax %network-byte-order
  (identifier-syntax (endianness big)))

(define (dce-uuid->string uuid)
  "Convert UUID, a 16-byte bytevector, to its string representation, something
like \"6b700d61-5550-48a1-874c-a3d86998990e\"."
  ;; See <https://tools.ietf.org/html/rfc4122>.
  (let ((time-low  (bytevector-uint-ref uuid 0 %network-byte-order 4))
        (time-mid  (bytevector-uint-ref uuid 4 %network-byte-order 2))
        (time-hi   (bytevector-uint-ref uuid 6 %network-byte-order 2))
        (clock-seq (bytevector-uint-ref uuid 8 %network-byte-order 2))
        (node      (bytevector-uint-ref uuid 10 %network-byte-order 6)))
    (format #f "~8,'0x-~4,'0x-~4,'0x-~4,'0x-~12,'0x"
            time-low time-mid time-hi clock-seq node)))

(define %uuid-rx
  ;; The regexp of a UUID.
  (make-regexp "^([[:xdigit:]]{8})-([[:xdigit:]]{4})-([[:xdigit:]]{4})-([[:xdigit:]]{4})-([[:xdigit:]]{12})$"))

(define (string->dce-uuid str)
  "Parse STR as a DCE UUID (see <https://tools.ietf.org/html/rfc4122>) and
return its contents as a 16-byte bytevector.  Return #f if STR is not a valid
UUID representation."
  (and=> (regexp-exec %uuid-rx str)
         (lambda (match)
           (letrec-syntax ((hex->number
                            (syntax-rules ()
                              ((_ index)
                               (string->number (match:substring match index)
                                               16))))
                           (put!
                            (syntax-rules ()
                              ((_ bv index (number len) rest ...)
                               (begin
                                 (bytevector-uint-set! bv index number
                                                       (endianness big) len)
                                 (put! bv (+ index len) rest ...)))
                              ((_ bv index)
                               bv))))
             (let ((time-low  (hex->number 1))
                   (time-mid  (hex->number 2))
                   (time-hi   (hex->number 3))
                   (clock-seq (hex->number 4))
                   (node      (hex->number 5))
                   (uuid      (make-bytevector 16)))
               (put! uuid 0
                     (time-low 4) (time-mid 2) (time-hi 2)
                     (clock-seq 2) (node 6)))))))


;;;
;;; ISO-9660.
;;;

;; <http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf>.

(define %iso9660-uuid-rx
  ;;                   Y                m                d                H                M                S                ss
  (make-regexp "^([[:digit:]]{4})-([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{2})$"))
(define (string->iso9660-uuid str)
  "Parse STR as a ISO9660 UUID (which is really a timestamp - see /dev/disk/by-uuid).
Return its contents as a 16-byte bytevector.  Return #f if STR is not a valid
ISO9660 UUID representation."
  (and=> (regexp-exec %iso9660-uuid-rx str)
         (lambda (match)
           (letrec-syntax ((match-numerals
                            (syntax-rules ()
                              ((_ index (name rest ...) body)
                               (let ((name (match:substring match index)))
                                 (match-numerals (+ 1 index) (rest ...) body)))
                              ((_ index () body)
                               body))))
            (match-numerals 1 (year month day hour minute second hundredths)
              (string->utf8 (string-append year month day
                                           hour minute second hundredths)))))))
(define (iso9660-uuid->string uuid)
  "Given an UUID bytevector, return its timestamp string."
  (define (digits->string bytes)
    (latin1->string bytes (lambda (c) #f)))
  (let* ((year (sub-bytevector uuid 0 4))
         (month (sub-bytevector uuid 4 2))
         (day (sub-bytevector uuid 6 2))
         (hour (sub-bytevector uuid 8 2))
         (minute (sub-bytevector uuid 10 2))
         (second (sub-bytevector uuid 12 2))
         (hundredths (sub-bytevector uuid 14 2))
         (parts (list year month day hour minute second hundredths)))
    (string-append (string-join (map digits->string parts) "-"))))


;;;
;;; exFAT/FAT32/FAT16.
;;;

(define-syntax %fat-endianness
  ;; Endianness of FAT32/FAT16 file systems.
  (identifier-syntax (endianness little)))

(define (fat-uuid->string uuid)
  "Convert FAT32/FAT16 UUID, a 4-byte bytevector, to its string representation."
  (let ((high  (bytevector-uint-ref uuid 0 %fat-endianness 2))
        (low (bytevector-uint-ref uuid 2 %fat-endianness 2)))
    (format #f "~:@(~4,'0x-~4,'0x~)" low high)))

(define %fat-uuid-rx
  (make-regexp "^([[:xdigit:]]{4})-([[:xdigit:]]{4})$"))

(define (string->fat-uuid str)
  "Parse STR, which is in FAT32/FAT16 format, and return a bytevector or #f."
  (match (regexp-exec %fat-uuid-rx str)
    (#f
     #f)
    (rx-match
     (uint-list->bytevector (list (string->number
                                   (match:substring rx-match 2) 16)
                                  (string->number
                                   (match:substring rx-match 1) 16))
                            %fat-endianness
                            2))))


;;;
;;; NTFS.
;;;

(define-syntax %ntfs-endianness
  ;; Endianness of NTFS file system.
  (identifier-syntax (endianness little)))

(define (ntfs-uuid->string uuid)
  "Convert NTFS UUID, a 8-byte bytevector, to its string representation."
  (format #f "~{~:@(~2,'0x~)~}" (reverse (bytevector->u8-list uuid))))

(define %ntfs-uuid-rx
  (make-regexp "^([[:xdigit:]]{16})$"))

(define (string->ntfs-uuid str)
  "Parse STR, which is in NTFS format, and return a bytevector or #f."
  (match (regexp-exec %ntfs-uuid-rx str)
    (#f
     #f)
    (rx-match
     (u8-list->bytevector
      (let loop ((str str)
                 (res '()))
        (if (string=? str "")
            res
            (loop (string-drop str 2)
                  (cons
                   (string->number (string-take str 2) 16)
                   res))))))))


;;;
;;; Generic interface.
;;;

(define string->ext2-uuid string->dce-uuid)
(define string->ext3-uuid string->dce-uuid)
(define string->ext4-uuid string->dce-uuid)
(define string->bcachefs-uuid string->dce-uuid)
(define string->btrfs-uuid string->dce-uuid)
(define string->f2fs-uuid string->dce-uuid)
(define string->jfs-uuid string->dce-uuid)
(define string->xfs-uuid string->dce-uuid)

(define-syntax vhashq
  (syntax-rules (=>)
    ((_)
     vlist-null)
    ((_ (key others ... => value) rest ...)
     (vhash-consq key value
                  (vhashq (others ... => value) rest ...)))
    ((_ (=> value) rest ...)
     (vhashq rest ...))))

(define %uuid-parsers
  (vhashq
   ('dce 'ext2 'ext3 'ext4 'bcachefs 'btrfs 'f2fs 'jfs 'xfs 'luks
         => string->dce-uuid)
   ('exfat 'fat32 'fat16 'fat => string->fat-uuid)
   ('ntfs => string->ntfs-uuid)
   ('iso9660 => string->iso9660-uuid)))

(define %uuid-printers
  (vhashq
   ('dce 'ext2 'ext3 'ext4 'bcachefs 'btrfs 'f2fs 'jfs 'xfs 'luks
         => dce-uuid->string)
   ('iso9660 => iso9660-uuid->string)
   ('exfat 'fat32 'fat16 'fat => fat-uuid->string)
   ('ntfs => ntfs-uuid->string)))

(define* (string->uuid str #:optional (type 'dce))
  "Parse STR as a UUID of the given TYPE.  On success, return the
corresponding bytevector; otherwise return #f."
  (match (vhash-assq type %uuid-parsers)
    (#f #f)
    ((_ . (? procedure? parse)) (parse str))))

;; High-level UUID representation that carries its type with it.
;;
;; This is necessary to serialize bytevectors with the right printer in some
;; circumstances.  For instance, GRUB "search --fs-uuid" command compares the
;; string representation of UUIDs, not the raw bytes; thus, when emitting a
;; GRUB 'search' command, we need to produce the right string representation
;; (see <https://debbugs.gnu.org/cgi/bugreport.cgi?msg=52;att=0;bug=27735>).
(define-record-type <uuid>
  (make-uuid type bv)
  uuid?
  (type  uuid-type)                               ;'dce | 'iso9660 | ...
  (bv    uuid-bytevector))

(define* (bytevector->uuid bv #:optional (type 'dce))
  "Return a UUID object make of BV and TYPE."
  (make-uuid type bv))

(define-syntax uuid
  (lambda (s)
    "Return the UUID object corresponding to the given UUID representation or
#f if the string could not be parsed."
    (syntax-case s (quote)
      ((_ str (quote type))
       (and (string? (syntax->datum #'str))
            (identifier? #'type))
       ;; A literal string: do the conversion at expansion time.
       (let ((bv (string->uuid (syntax->datum #'str)
                               (syntax->datum #'type))))
         (unless bv
           (syntax-violation 'uuid "invalid UUID" s))
         #`(make-uuid 'type #,(datum->syntax s bv))))
      ((_ str)
       (string? (syntax->datum #'str))
       #'(uuid str 'dce))
      ((_ str)
       #'(let ((bv (string->uuid str 'dce)))
           (and bv (make-uuid 'dce bv))))
      ((_ str type)
       #'(let ((bv (string->uuid str type)))
           (and bv (make-uuid type bv)))))))

(define uuid->string
  ;; Convert the given bytevector or UUID object, to the corresponding UUID
  ;; string representation.
  (match-lambda*
    (((? bytevector? bv))
     (uuid->string bv 'dce))
    (((? bytevector? bv) type)
     (match (vhash-assq type %uuid-printers)
       (#f #f)
       ((_ . (? procedure? unparse)) (unparse bv))))
    (((? uuid? uuid))
     (uuid->string (uuid-bytevector uuid) (uuid-type uuid)))))

(define uuid=?
  ;; Return true if A is equal to B, comparing only the actual bits.
  (match-lambda*
    (((? bytevector? a) (? bytevector? b))
     (bytevector=? a b))
    (((? uuid? a) (? bytevector? b))
     (bytevector=? (uuid-bytevector a) b))
    (((? uuid? a) (? uuid? b))
     (bytevector=? (uuid-bytevector a) (uuid-bytevector b)))
    (((or (? uuid? a) (? bytevector? a)) (or (? uuid? b) (? bytevector? b)))
     (uuid=? b a))))
ctivated root profile at ${GUIX_PROFILE}" } sys_delete_store() { _msg "${INF}removing /var/guix" rm -rf /var/guix _msg "${INF}removing /gnu" rm -rf /gnu _msg "${INF}removing ~root/.config/guix" rm -rf ~root/.config/guix } sys_create_build_user() { # Create the group and user accounts for build users. _debug "--- [ ${FUNCNAME[0]} ] ---" if getent group guixbuild > /dev/null; then _msg "${INF}group guixbuild exists" else groupadd --system guixbuild _msg "${PAS}group <guixbuild> created" fi if getent group kvm > /dev/null; then _msg "${INF}group kvm exists and build users will be added to it" local KVMGROUP=,kvm fi for i in $(seq -w 1 10); do if id "guixbuilder${i}" &>/dev/null; then _msg "${INF}user is already in the system, reset" usermod -g guixbuild -G guixbuild${KVMGROUP} \ -d /var/empty -s "$(which nologin)" \ -c "Guix build user $i" \ "guixbuilder${i}"; else useradd -g guixbuild -G guixbuild${KVMGROUP} \ -d /var/empty -s "$(which nologin)" \ -c "Guix build user $i" --system \ "guixbuilder${i}"; _msg "${PAS}user added <guixbuilder${i}>" fi done } sys_delete_build_user() { for i in $(seq -w 1 10); do if id -u "guixbuilder${i}" &>/dev/null; then userdel -f guixbuilder${i} fi done _msg "${INF}delete group guixbuild" if getent group guixbuild &>/dev/null; then groupdel -f guixbuild fi } sys_enable_guix_daemon() { # Run the daemon, and set it to automatically start on boot. local info_path local local_bin local var_guix _debug "--- [ ${FUNCNAME[0]} ] ---" info_path="/usr/local/share/info" local_bin="/usr/local/bin" var_guix="/var/guix/profiles/per-user/root/current-guix" case "$INIT_SYS" in upstart) { initctl reload-configuration; cp ~root/.config/guix/current/lib/upstart/system/guix-daemon.conf \ /etc/init/ && configure_substitute_discovery /etc/init/guix-daemon.conf && start guix-daemon; } && _msg "${PAS}enabled Guix daemon via upstart" ;; systemd) { install_unit() { local dest="/etc/systemd/system/$1" rm -f "$dest" cp ~root/.config/guix/current/lib/systemd/system/"$1" "$dest" chmod 664 "$dest" systemctl daemon-reload systemctl enable "$1" } install_unit guix-daemon.service configure_substitute_discovery \ /etc/systemd/system/guix-daemon.service # Install after guix-daemon.service to avoid a harmless warning. # systemd .mount units must be named after the target directory. # Here we assume a hard-coded name of /gnu/store. install_unit gnu-store.mount systemctl daemon-reload && systemctl start guix-daemon; } && _msg "${PAS}enabled Guix daemon via systemd" ;; sysv-init) { mkdir -p /etc/init.d; cp ~root/.config/guix/current/etc/init.d/guix-daemon \ /etc/init.d/guix-daemon; chmod 775 /etc/init.d/guix-daemon; configure_substitute_discovery /etc/init.d/guix-daemon update-rc.d guix-daemon defaults && update-rc.d guix-daemon enable && service guix-daemon start; } && _msg "${PAS}enabled Guix daemon via sysv" ;; openrc) { mkdir -p /etc/init.d; cp ~root/.config/guix/current/etc/openrc/guix-daemon \ /etc/init.d/guix-daemon; chmod 775 /etc/init.d/guix-daemon; configure_substitute_discovery /etc/init.d/guix-daemon rc-update add guix-daemon default && rc-service guix-daemon start; } && _msg "${PAS}enabled Guix daemon via OpenRC" ;; NA|*) _msg "${ERR}unsupported init system; run the daemon manually:" echo " ~root/.config/guix/current/bin/guix-daemon --build-users-group=guixbuild" ;; esac _msg "${INF}making the guix command available to other users" [ -e "$local_bin" ] || mkdir -p "$local_bin" ln -sf "${var_guix}/bin/guix" "$local_bin" [ -e "$info_path" ] || mkdir -p "$info_path" for i in "${var_guix}"/share/info/*; do ln -sf "$i" "$info_path" done } sys_delete_guix_daemon() { # Disabled, stop and remove the various guix daemons. local info_path local local_bin local var_guix _debug "--- [ $FUNCNAME ] ---" info_path="/usr/local/share/info" local_bin="/usr/local/bin" case "$INIT_SYS" in upstart) _msg "${INF}stopping guix-daemon" stop guix-daemon _msg "${INF}removing guix-daemon" rm /etc/init/guix-daemon.conf ;; systemd) if [ -f /etc/systemd/system/guix-daemon.service ]; then _msg "${INF}disabling guix-daemon" systemctl disable guix-daemon _msg "${INF}stopping guix-daemon" systemctl stop guix-daemon _msg "${INF}removing guix-daemon" rm -f /etc/systemd/system/guix-daemon.service fi if [ -f /etc/systemd/system/gnu-store.mount ]; then _msg "${INF}disabling gnu-store.mount" systemctl disable gnu-store.mount _msg "${INF}stopping gnu-store.mount" systemctl stop gnu-store.mount _msg "${INF}removing gnu-store.mount" rm -f /etc/systemd/system/gnu-store.mount fi systemctl daemon-reload ;; sysv-init) update-rc.d guix-daemon disable service guix-daemon stop rm -rf /etc/init.d/guix-daemon ;; NA|*) _msg "${ERR}unsupported init system; disable, stop and remove the daemon manually:" echo " ~root/.config/guix/current/bin/guix-daemon --build-users-group=guixbuild" ;; esac _msg "${INF}removing $local_bin/guix" rm -f "$local_bin"/guix _msg "${INF}removing $info_path/guix*" rm -f "$info_path"/guix* } sys_authorize_build_farms() { # authorize the public key(s) of the build farm(s) local hosts=( bordeaux.guix.gnu.org ci.guix.gnu.org ) if prompt_yes_no "Permit downloading pre-built package binaries from the \ project's build farms?"; then for host in "${hosts[@]}"; do local key=~root/.config/guix/current/share/guix/$host.pub [ -f "$key" ] \ && guix archive --authorize < "$key" \ && _msg "${PAS}Authorized public key for $host" done else _msg "${INF}Skipped authorizing build farm public keys" fi } sys_create_init_profile() { # Define for better desktop integration # This will not take effect until the next shell or desktop session! [ -d "/etc/profile.d" ] || mkdir /etc/profile.d # Just in case cat <<"EOF" > /etc/profile.d/zzz-guix.sh # Explicitly initialize XDG base directory variables to ease compatibility # with Guix System: see <https://issues.guix.gnu.org/56050#3>. export XCURSOR_PATH="${XCURSOR_PATH:-/usr/local/share/icons:/usr/share/icons}" export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" export XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}" export XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}" export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}" # no default for XDG_RUNTIME_DIR (depends on foreign distro for semantics) # _GUIX_PROFILE: `guix pull` profile _GUIX_PROFILE="$HOME/.config/guix/current" export PATH="$_GUIX_PROFILE/bin${PATH:+:}$PATH" # GUIX_PROFILE: User's default profile and home profile GUIX_PROFILE="$HOME/.guix-profile" [ -f "$GUIX_PROFILE/etc/profile" ] && . "$GUIX_PROFILE/etc/profile" [ -L "$GUIX_PROFILE" ] && \ GUIX_LOCPATH="$GUIX_PROFILE/lib/locale${GUIX_LOCPATH:+:}$GUIX_LOCPATH" # Export INFOPATH so that the updated info pages can be found # and read by both /usr/bin/info and/or $GUIX_PROFILE/bin/info # When INFOPATH is unset, add a trailing colon so that Emacs # searches 'Info-default-directory-list'. export INFOPATH="$_GUIX_PROFILE/share/info:$GUIX_PROFILE/share/info:$INFOPATH" GUIX_PROFILE="$HOME/.guix-home/profile" [ -f "$GUIX_PROFILE/etc/profile" ] && . "$GUIX_PROFILE/etc/profile" [ -L "$GUIX_PROFILE" ] && \ GUIX_LOCPATH="$GUIX_PROFILE/lib/locale${GUIX_LOCPATH:+:}$GUIX_LOCPATH" export GUIX_LOCPATH # Make Guix modules available export GUILE_LOAD_PATH="$_GUIX_PROFILE/share/guile/site/3.0${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH" export GUILE_LOAD_COMPILED_PATH="$_GUIX_PROFILE/lib/guile/3.0/site-ccache${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH" EOF } sys_create_shell_completion() { # Symlink supported shell completions system-wide var_guix=/var/guix/profiles/per-user/root/current-guix bash_completion=/etc/bash_completion.d zsh_completion=/usr/share/zsh/site-functions fish_completion=/usr/share/fish/vendor_completions.d { # Just in case for dir_shell in $bash_completion $zsh_completion $fish_completion; do [ -d "$dir_shell" ] || mkdir -p $dir_shell done; # Don't use globing here as we also need to delete the files when # uninstalling Guix ln -sf ${var_guix}/etc/bash_completion.d/guix "$bash_completion"; ln -sf ${var_guix}/etc/bash_completion.d/guix-daemon "$bash_completion"; ln -sf ${var_guix}/share/zsh/site-functions/_guix "$zsh_completion"; ln -sf ${var_guix}/share/fish/vendor_completions.d/guix.fish "$fish_completion"; } && _msg "${PAS}installed shell completion" } sys_delete_shell_completion() { # Symlink supported shell completions system-wide var_guix=/var/guix/profiles/per-user/root/current-guix bash_completion=/etc/bash_completion.d zsh_completion=/usr/share/zsh/site-functions fish_completion=/usr/share/fish/vendor_completions.d _msg "${INF}removing shell completion" rm -f "$bash_completion"/guix; rm -f "$bash_completion"/guix-daemon; rm -f "$zsh_completion"/_guix; rm -f "$fish_completion"/guix.fish; } sys_customize_bashrc() { prompt_yes_no "Customize users Bash shell prompt for Guix?" || return 0 for bashrc in /home/*/.bashrc /root/.bashrc; do test -f "$bashrc" || continue grep -Fq '$GUIX_ENVIRONMENT' "$bashrc" && continue cp "${bashrc}" "${bashrc}.bak" echo ' # Automatically added by the Guix install script. if [ -n "$GUIX_ENVIRONMENT" ]; then if [[ $PS1 =~ (.*)"\\$" ]]; then PS1="${BASH_REMATCH[1]} [env]\\\$ " fi fi ' >> "$bashrc" done _msg "${PAS}Bash shell prompt successfully customized for Guix" } sys_maybe_setup_selinux() { if ! [ -f /sys/fs/selinux/policy ] then return fi local c for c in semodule restorecon do if ! command -v "$c" &>/dev/null then return fi done prompt_yes_no "Install SELinux policy that might be required to run guix-daemon?" \ || return 0 local var_guix=/var/guix/profiles/per-user/root/current-guix semodule -i "${var_guix}/share/selinux/guix-daemon.cil" restorecon -R /gnu /var/guix } sys_delete_init_profile() { _msg "${INF}removing /etc/profile.d/guix.sh" rm -f /etc/profile.d/guix.sh } sys_delete_user_profiles() { _msg "${INF}removing ~root/.guix-profile" rm -f ~root/.guix-profile rm -rf ~root/.cache/guix _msg "${INF}removing .guix-profile, .cache/guix and .config/guix of all /home users" for user in `ls -1 /home`; do rm -f /home/$user/.guix-profile rm -rf /home/$user/.cache/guix rm -rf /home/$user/.config/guix done } welcome() { local uninstall_flag="$1" local char cat<<"EOF" ░░░ ░░░ ░░▒▒░░░░░░░░░ ░░░░░░░░░▒▒░░ ░░▒▒▒▒▒░░░░░░░ ░░░░░░░▒▒▒▒▒░ ░▒▒▒░░▒▒▒▒▒ ░░░░░░░▒▒░ ░▒▒▒▒░ ░░░░░░ ▒▒▒▒▒ ░░░░░░ ▒▒▒▒▒ ░░░░░ ░▒▒▒▒▒ ░░░░░ ▒▒▒▒▒ ░░░░░ ▒▒▒▒▒ ░░░░░ ░▒▒▒▒▒░░░░░ ▒▒▒▒▒▒░░░ ▒▒▒▒▒▒░ _____ _ _ _ _ _____ _ / ____| \ | | | | | / ____| (_) | | __| \| | | | | | | __ _ _ ___ __ | | |_ | . ' | | | | | | |_ | | | | \ \/ / | |__| | |\ | |__| | | |__| | |_| | |> < \_____|_| \_|\____/ \_____|\__,_|_/_/\_\ https://www.gnu.org/software/guix/ EOF if [ '--uninstall' = "$uninstall_flag" ]; then echo "${WARN}This script will uninstall GNU Guix from your system" echo "To install, run this script with no parameters." else echo "This script installs GNU Guix on your system" echo "To uninstall, pass in the '--uninstall' parameter." fi # Don't use ‘read -p’ here! It won't display when run non-interactively. echo -n "Press return to continue..."$'\r' if ! read -r char; then echo die "Can't read standard input. Hint: don't pipe scripts into a shell." fi if [ "$char" ]; then echo echo "...that ($char) was not a return!" _msg "${WAR}Use newlines to automate installation, e.g.: yes '' | ${0##*/}" _msg "${WAR}Any other method is unsupported and likely to break in future." fi } main_install() { local tmp_path welcome _msg "Starting installation ($(date))" chk_term chk_init_sys add_init_sys_require chk_require "${REQUIRE[@]}" chk_gpg_keyring chk_sys_arch chk_sys_nscd _msg "${INF}system is ${ARCH_OS}" umask 0022 tmp_path="$(mktemp -t -d guix.XXXXXX)" if [ -z "${GUIX_BINARY_FILE_NAME}" ]; then guix_get_bin_list "${GNU_URL}" guix_get_bin "${GNU_URL}" "${BIN_VER}" "$tmp_path" GUIX_BINARY_FILE_NAME=${BIN_VER}.tar.xz else if ! [[ $GUIX_BINARY_FILE_NAME =~ $ARCH_OS ]]; then _err "$ARCH_OS not in ${GUIX_BINARY_FILE_NAME}; aborting" fi _msg "${INF}Using manually provided binary ${GUIX_BINARY_FILE_NAME}" GUIX_BINARY_FILE_NAME=$(realpath "$GUIX_BINARY_FILE_NAME") fi sys_create_store "${GUIX_BINARY_FILE_NAME}" "${tmp_path}" sys_create_build_user sys_maybe_setup_selinux sys_enable_guix_daemon sys_authorize_build_farms sys_create_init_profile sys_create_shell_completion sys_customize_bashrc _msg "${INF}cleaning up ${tmp_path}" rm -r "${tmp_path}" _msg "${PAS}Guix has successfully been installed!" _msg "${INF}Run 'info guix' to read the manual." # Required to source /etc/profile in desktop environments. _msg "${INF}Please log out and back in to complete the installation." } main_uninstall() { welcome --uninstall _msg "Starting uninstall process ($(date))" chk_term chk_require "${REQUIRE[@]}" # it's ok to leave the gpg key chk_init_sys chk_sys_arch _msg "${INF}system is ${ARCH_OS}" # stop the build, package system. sys_delete_guix_daemon # stop people from accessing their profiles. sys_delete_user_profiles # kill guix off all the guts of guix sys_delete_store # clean up the system sys_delete_init_profile sys_delete_build_user sys_delete_shell_completion # these directories are created on the fly during usage. _msg "${INF}removing /etc/guix" rm -rf /etc/guix _msg "${INF}removing /var/log/guix" rm -rf /var/log/guix _msg "${PAS}Guix has successfully been uninstalled!" } main() { # expect no parameters # or '--uninstall' if [ 0 -eq $# ]; then main_install else local uninstall_flag="$1" if [ '--uninstall' = "${uninstall_flag}" ]; then main_uninstall else echo "unsupported parameters: $@" exit 1 fi fi } main "$@"