fordon_greeman / pkger

Branch: c2d1d35
Switch branches/tags
main
theidioticdev feat: support more than just git, guix pkg test, port bs from c to shell c2d1d35 18d ago
1152 lines · 34.7 KB Raw Blame History
1 #!/bin/sh
2 # GNU Guix --- Functional package management for GNU
3 # Copyright © 2017 sharlatan <sharlatanus@gmail.com>
4 # Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
5 # Copyright © 2018 Efraim Flashner <efraim@flashner.co.il>
6 # Copyright © 2019–2020, 2022 Tobias Geerinckx-Rice <me@tobias.gr>
7 # Copyright © 2020 Morgan Smith <Morgan.J.Smith@outlook.com>
8 # Copyright © 2020 Simon Tournier <zimon.toutoune@gmail.com>
9 # Copyright © 2020 Daniel Brooks <db48x@db48x.net>
10 # Copyright © 2021 Jakub Kądziołka <kuba@kadziolka.net>
11 # Copyright © 2021 Chris Marusich <cmmarusich@gmail.com>
12 # Copyright © 2021, 2022, 2023 Maxim Cournoyer <maxim@guixotic.coop>
13 # Copyright © 2022 Prafulla Giri <prafulla.giri@protonmail.com>
14 # Copyright © 2023 Andrew Tropin <andrew@trop.in>
15 # Copyright © 2020 David A. Redick <david.a.redick@gmail.com>
16 # Copyright © 2024 Janneke Nieuwenhuizen <janneke@gnu.org>
17 # Copyright © 2024 Tomas Volf <~@wolfsden.cz>
18 # Copyright © 2024 Richard Sent <richard@freakingpenguin.com>
19 # Copyright © 2025 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
20 # Copyright © 2025 Noé Lopez <noelopez@free.fr>
21 # Copyright © 2026 Owen T. Heisler <writer@owenh.net>
22 #
23 # This file is part of GNU Guix.
24 #
25 # GNU Guix is free software; you can redistribute it and/or modify it
26 # under the terms of the GNU General Public License as published by
27 # the Free Software Foundation; either version 3 of the License, or (at
28 # your option) any later version.
29 #
30 # GNU Guix is distributed in the hope that it will be useful, but
31 # WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 # GNU General Public License for more details.
34 #
35 # You should have received a copy of the GNU General Public License
36 # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
37
38 # We require Bash but for portability we'd rather not use /bin/bash or
39 # /usr/bin/env in the shebang, hence this hack.
40
41 # Environment variables
42 #
43 # GUIX_BINARY_FILE_NAME
44 #
45 # Can be used to override the automatic download mechanism and point
46 # to a local Guix binary archive filename like
47 # "/tmp/guix-binary-1.4.0rc2.armhf-linux.tar.xz"
48 #
49 # GUIX_ALLOW_OVERWRITE
50 #
51 # Instead of aborting to avoid overwriting a previous installations,
52 # allow copying over /var/guix or /gnu. This can be useful when the
53 # installation required the user to extract Guix packs under /gnu to
54 # satisfy its dependencies.
55
56 # shellcheck shell=bash
57
58 # shellcheck disable=2268 # try to support vintage shells
59 if [ "x$BASH_VERSION" = "x" ]; then
60 exec bash "$0" "$@"
61 fi
62
63 set -eo pipefail
64
65 [ "$UID" -eq 0 ] || { echo "This script must be run as root."; exit 1; }
66
67 REQUIRE=(
68 "dirname"
69 "readlink"
70 "wget"
71 "gpg"
72 "grep"
73 "sed"
74 "sort"
75 "getent"
76 "mktemp"
77 "rm"
78 "chmod"
79 "uname"
80 "groupadd"
81 "groupdel"
82 "useradd"
83 "userdel"
84 "tail"
85 "tr"
86 "xz"
87 )
88
89 # Add variables using form FOO_INIT_REQUIRE when init system FOO dependencies
90 # should be checked.
91 # shellcheck disable=2034 # interpolated by add_init_sys_require
92 SYSV_INIT_REQUIRE=(
93 "daemonize"
94 )
95
96 # Unprivileged guix-daemon requires 'newgidmap'.
97 SYSTEMD_REQUIRE=(
98 "newgidmap"
99 )
100
101 PAS=$'[ \033[32;1mPASS\033[0m ] '
102 ERR=$'[ \033[31;1mFAIL\033[0m ] '
103 WAR=$'[ \033[33;1mWARN\033[0m ] '
104 INF="[ INFO ] "
105
106 DEBUG=0
107 GNU_URL="https://ftp.gnu.org/gnu/guix/"
108
109 # The following associative array holds set of GPG keys used to sign the
110 # releases, keyed by their corresponding Codeberg user name.
111 declare -A GPG_SIGNING_KEYS
112 GPG_SIGNING_KEYS["civodul"]=3CE464558A84FDC69DB40CFB090B11993D9AEBB5 # ludo
113 GPG_SIGNING_KEYS["apteryx"]=27D586A4F8900854329FF09F1260E46482E63562 # maxim
114 GPG_SIGNING_KEYS["efraim"]=A28BF40C3E551372662D14F741AAE7DCCA3D8351 # efraim
115 PUBLIC_KEYSERVERS="keys.openpgp.org pgpkeys.eu keyserver.ubuntu.com"
116
117 # ------------------------------------------------------------------------------
118 #+UTILITIES
119
120 _err()
121 { # All errors go to stderr.
122 printf "[%s]: ${ERR}%s\n" "$(date +%s.%3N)" "$1"
123 }
124
125 _msg()
126 { # Default message to stdout.
127 printf "[%s]: %s\n" "$(date +%s.%3N)" "$1"
128 }
129
130 _msg_pass()
131 {
132 _msg "$PAS$1"
133 }
134
135 _msg_warn()
136 {
137 _msg "$WAR$1"
138 }
139
140 _msg_info()
141 {
142 _msg "$INF$1"
143 }
144
145 _debug()
146 {
147 if [ "${DEBUG}" = '1' ]; then
148 printf "[%s]: %s\n" "$(date +%s.%3N)" "$1"
149 fi
150 }
151
152 die()
153 {
154 _err "$*"
155 exit 1
156 }
157
158 # Return true if user answered yes, false otherwise. The prompt is
159 # yes-biased, that is, when the user simply enter newline, it is equivalent to
160 # answering "yes".
161 # $1: The prompt question.
162 prompt_yes_no() {
163 local -l yn
164 read -rp "$1 [Y/n]" yn
165 [[ ! $yn || $yn = y || $yn = yes ]] || return 1
166 }
167
168 chk_require()
169 { # Check that every required command is available.
170 declare -a warn
171 local c
172
173 _debug "--- [ ${FUNCNAME[0]} ] ---"
174
175 for c in "$@"; do
176 command -v "$c" &>/dev/null || warn+=("$c")
177 done
178
179 [ "${#warn}" -ne 0 ] && die "Missing commands: ${warn[*]}."
180
181 _msg_pass "verification of required commands completed"
182 }
183
184 add_init_sys_require()
185 { # Add the elements of FOO_INIT_SYS to REQUIRE
186
187 # Convert the init system command name captured in INIT_SYS in
188 # chk_init_sys to uppercase, with hyphens replaced by underscores.
189 local init_require=${INIT_SYS^^}_REQUIRE[@]
190 init_require=${init_require//-/_}
191
192 if [[ -n "$init_require" ]]; then
193 # Have to add piecemeal because ${!foo[@]} performs direct array key
194 # expansion, not indirect plain array expansion.
195 for r in "${!init_require}"; do
196 REQUIRE+=("$r")
197 done
198 fi
199 }
200
201 chk_gpg_keyring()
202 { # Check whether the Guix release signing public key is present.
203 _debug "--- [ ${FUNCNAME[0]} ] ---"
204 local user_id
205 local gpg_key_id
206 local exit_flag
207
208 for user_id in "${!GPG_SIGNING_KEYS[@]}"; do
209 gpg_key_id=${GPG_SIGNING_KEYS[$user_id]}
210 codeberg_username=$user_id
211 # Without --dry-run this command will create a ~/.gnupg owned by root on
212 # systems where gpg has never been used, causing errors and confusion.
213 if gpg --dry-run --list-keys "$gpg_key_id" >/dev/null 2>&1; then
214 continue
215 fi
216 if prompt_yes_no "${INF}The following OpenPGP public key is \
217 required to verify the Guix binary signature: $gpg_key_id.
218 Would you like me to fetch it for you?"; then
219 if wget "https://codeberg.org/$codeberg_username.gpg" \
220 --tries=1 --timeout=30 --no-verbose -O- | gpg --import -; then
221 continue
222 fi
223
224 key_obtained=false
225 # Try to fetch keys from an available keyserver
226 for key_server in $PUBLIC_KEYSERVERS; do
227 if gpg --keyserver $key_server --recv-key $gpg_key_id; then
228 key_obtained=true
229 break
230 fi
231 done
232
233 if $key_obtained; then
234 continue
235 fi
236 fi
237 # If we reach this point, the key is (still) missing. Report further
238 # missing keys, if any, but then abort the installation.
239 _err "Missing OpenPGP public key ($gpg_key_id).
240 Fetch it with codeberg username:
241
242 wget \"https://codeberg.org/$codeberg_username.gpg\" -O - | \
243 sudo -i gpg --import -
244
245 If this fails, try to fetch it via a keyserver:
246
247 for key_server in $PUBLIC_KEYSERVERS; do
248 if sudo -i gpg --keyserver $key_server --recv-key $gpg_key_id; then
249 break
250 fi
251 done"
252 exit_flag=yes
253 done
254 if [ "$exit_flag" = yes ]; then
255 exit 1
256 fi
257 }
258
259 chk_term()
260 { # Check for ANSI terminal for color printing.
261 if [ -t 2 ]; then
262 if [ "${TERM+set}" = 'set' ]; then
263 case "$TERM" in
264 xterm*|rxvt*|urxvt*|linux*|vt*|eterm*|screen*)
265 ;;
266 *)
267 ERR="[ FAIL ] "
268 PAS="[ PASS ] "
269 WAR="[ WARN ] "
270 ;;
271 esac
272 fi
273 fi
274 }
275
276 chk_init_sys()
277 { # Return init system type name.
278 if [[ $(/sbin/init --version 2>/dev/null) =~ upstart ]]; then
279 _msg_info "init system is: upstart"
280 INIT_SYS="upstart"
281 return 0
282 elif [[ $(systemctl 2>/dev/null) =~ -\.mount ]]; then
283 _msg_info "init system is: systemd"
284 INIT_SYS="systemd"
285 return 0
286 elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then
287 _msg_info "init system is: sysv-init"
288 INIT_SYS="sysv-init"
289 return 0
290 elif [[ $(openrc --version 2>/dev/null) =~ \(OpenRC ]]; then
291 _msg_info "init system is: OpenRC"
292 INIT_SYS="openrc"
293 return 0
294 else
295 INIT_SYS="NA"
296 _err "Init system could not be detected."
297 fi
298 }
299
300 chk_sys_arch()
301 { # Check for operating system and architecture type.
302 local os
303 local arch
304
305 os="$(uname -s)"
306 arch="$(uname -m)"
307
308 case "$arch" in
309 i386 | i486 | i686 | i786 | x86)
310 local arch=i686
311 ;;
312 x86_64 | x86-64 | x64 | amd64)
313 local arch=x86_64
314 ;;
315 aarch64)
316 local arch=aarch64
317 ;;
318 armv7l)
319 local arch=armhf
320 ;;
321 ppc64le | powerpc64le)
322 local arch=powerpc64le
323 ;;
324 riscv64)
325 local arch=riscv64
326 ;;
327 *)
328 die "Unsupported CPU type: ${arch}"
329 esac
330
331 case "$os" in
332 Linux | linux)
333 local os=linux
334 ;;
335 *)
336 die "Your operation system (${os}) is not supported."
337 esac
338
339 ARCH_OS="${arch}-${os}"
340 }
341
342 chk_sys_nscd()
343 { # Check if nscd is up and suggest to start it or install it
344 if [ "$(type -P pidof)" ]; then
345 if [ ! "$(pidof nscd)" ]; then
346 _msg_warn "We recommend installing and/or starting your distribution 'nscd' service"
347 _msg_warn "Please read 'info guix \"Application Setup\"' about \"Name Service Switch\""
348 fi
349 else
350 _msg_info "We cannot determine if your distribution 'nscd' service is running"
351 _msg_info "Please read 'info guix \"Application Setup\"' about \"Name Service Switch\""
352 fi
353 }
354
355 chk_existing()
356 { # Avoid clobbering existing installations.
357 _debug "--- [ ${FUNCNAME[0]} ] ---"
358
359 if [[ -e /var/guix && -e /gnu ]]; then
360 if [ -n "$GUIX_ALLOW_OVERWRITE" ]; then
361 _msg_warn "Overwriting existing installation!"
362 else
363 die "A previous Guix installation was found. Refusing to overwrite."
364 fi
365 fi
366 }
367
368 # Configure substitute discovery according to user's preferences.
369 # $1 is the installed service file to edit.
370 configure_substitute_discovery() {
371 if grep -q -- '--discover=no' "$1" && \
372 prompt_yes_no "Would you like the Guix daemon to automatically \
373 discover substitute servers on the local network?"; then
374 sed -i 's/--discover=no/--discover=yes/' "$1"
375 fi
376 }
377
378 # ------------------------------------------------------------------------------
379 #+MAIN
380
381 guix_get_bin_list()
382 { # Scan GNU archive and save list of binaries
383 local gnu_url="$1"
384 local -a bin_ver_ls
385 local latest_ver
386 local default_ver
387
388 _debug "--- [ ${FUNCNAME[0]} ] ---"
389
390 # Filter only version and architecture
391 bin_ver_ls=("$(wget "$gnu_url" --no-verbose -O- \
392 | sed -n -e 's/.*guix-binary-\([0-9.]*[a-z0-9]*\)\..*.tar.xz.*/\1/p' \
393 | sort -Vu)")
394
395 latest_ver="$(echo "${bin_ver_ls[0]}" \
396 | grep -oE "([0-9]{1,2}\.){2}[0-9]{1,2}[a-z0-9]*" \
397 | tail -n1)"
398
399 default_ver="guix-binary-${latest_ver}.${ARCH_OS}"
400
401 if [[ "${#bin_ver_ls}" -ne "0" ]]; then
402 _msg_pass "Release for your system: ${default_ver}"
403 else
404 die "Could not obtain list of Guix releases."
405 fi
406
407 # Use default to download according to the list and local ARCH_OS.
408 BIN_VER="${default_ver}"
409 }
410
411 guix_get_bin()
412 { # Download and verify binary package.
413 local url="$1"
414 local bin_ver="$2"
415 local dl_path="$3"
416 local wget_args=()
417
418 _debug "--- [ ${FUNCNAME[0]} ] ---"
419
420 _msg_info "Downloading Guix release archive"
421
422 wget --help | grep -q '\--show-progress' \
423 && wget_args=("--no-verbose" "--show-progress")
424
425 if wget "${wget_args[@]}" -P "$dl_path" \
426 "${url}/${bin_ver}.tar.xz" "${url}/${bin_ver}.tar.xz.sig"; then
427 _msg_pass "download completed."
428 else
429 die "could not download ${url}/${bin_ver}.tar.xz."
430 fi
431
432 pushd "${dl_path}" >/dev/null
433 if gpg --verify "${bin_ver}.tar.xz.sig" >/dev/null 2>&1; then
434 _msg_pass "Signature is valid."
435 popd >/dev/null
436 else
437 die "could not verify the signature."
438 fi
439 }
440
441 sys_create_store()
442 { # Unpack and install /gnu/store and /var/guix
443 local pkg="$1"
444 local tmp_path="$2"
445
446 cd "$tmp_path"
447 _msg_info "Installing /var/guix and /gnu..."
448 # Strip (skip) the leading ‘.’ component, which fails on read-only ‘/’.
449 #
450 # TODO: Eventually extract with ‘--owner=guix-daemon’ when installing
451 # and unprivileged guix-daemon service; for now, this script may install
452 # from both an old release that does not support unprivileged guix-daemon
453 # and a new release that does, so ‘chown -R’ later if needed.
454 tar --extract --strip-components=1 --file "$pkg" -C /
455
456 _msg_info "Linking the root user's profile"
457 mkdir -p ~root/.config/guix
458 ln -sf /var/guix/profiles/per-user/root/current-guix \
459 ~root/.config/guix/current
460
461 GUIX_PROFILE=~root/.config/guix/current
462 # The profile just prepends to search paths, which is not needed for
463 # effective linting.
464 # shellcheck disable=SC1091
465 source "${GUIX_PROFILE}/etc/profile"
466 _msg_pass "activated root profile at ${GUIX_PROFILE}"
467 }
468
469 sys_delete_store()
470 {
471 _msg_info "removing /var/guix"
472 rm -rf /var/guix
473
474 _msg_info "removing /gnu"
475 rm -rf /gnu
476
477 _msg_info "removing ~root/.config/guix"
478 rm -rf ~root/.config/guix
479 }
480
481 create_account()
482 {
483 local user="$1"
484 local group="$2"
485 local supplementary_groups="$3"
486 local comment="$4"
487
488 if id "$user" &>/dev/null; then
489 _msg_info "user '$user' is already in the system, reset"
490 usermod -g "$group" -G "$supplementary_groups" \
491 -d /var/empty -s "$(command -v nologin)" \
492 -c "$comment" "$user"
493 else
494 useradd -g "$group" -G "$supplementary_groups" \
495 -d /var/empty -s "$(command -v nologin)" \
496 -c "$comment" --system "$user"
497 _msg_pass "user added <$user>"
498 fi
499 }
500
501 install_unprivileged_daemon()
502 { # Return true when installing guix-daemon running without privileges.
503 [ "$INIT_SYS" = systemd ] && \
504 grep -q "User=guix-daemon" \
505 ~root/.config/guix/current/lib/systemd/system/guix-daemon.service
506 }
507
508 sys_create_build_user()
509 { # Create the group and user accounts for build users.
510
511 _debug "--- [ ${FUNCNAME[0]} ] ---"
512
513 if getent group kvm > /dev/null; then
514 _msg_info "group kvm exists and build users will be added to it"
515 local KVMGROUP=,kvm
516 fi
517
518 if install_unprivileged_daemon
519 then
520 _msg_info "installing guix-daemon to run as an unprivileged user"
521
522 # Installing guix-daemon to run as a non-root user requires
523 # unprivileged user namespaces.
524 if [ -f /proc/sys/kernel/unprivileged_userns_clone ] \
525 && [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -ne 1 ]
526 then
527 echo 1 > /proc/sys/kernel/unprivileged_userns_clone || \
528 _err "failed to enable unprivileged user namespaces"
529
530 _msg_warn "Unprivileged user namespaces were disabled and have been enabled now."
531 _msg_warn "This Linux feature is required by guix-daemon. To enable it permanently, run:"
532 _msg_warn ' echo 1 > /proc/sys/kernel/unprivileged_userns_clone'
533 _msg_warn "from the relevant startup script."
534 fi
535
536
537 if getent group guix-daemon > /dev/null; then
538 _msg_info "group guix-daemon exists"
539 else
540 groupadd --system guix-daemon
541 _msg_pass "group guix-daemon created"
542 fi
543
544 create_account guix-daemon guix-daemon \
545 guix-daemon"$KVMGROUP" \
546 "Unprivileged Guix Daemon User"
547
548 if getent group kvm > /dev/null; then
549 # Allow 'newgidmap' to map the "kvm" group.
550 local kvmgid="$(getent group kvm | cut -f3 -d:)"
551 _msg_info "allowing kvm mapping (GID $kvmgid) for unprivileged guix-daemon"
552 echo "guix-daemon:$kvmgid:1" >> /etc/subgid
553 fi
554
555 # ‘tar xf’ creates root:root files. Change that.
556 chown -R guix-daemon:guix-daemon /gnu /var/guix
557 chown -R root:root /var/guix/profiles/per-user/root
558
559 # The unprivileged daemon cannot create the log directory by itself.
560 mkdir -p /var/log/guix
561 chown guix-daemon:guix-daemon /var/log/guix
562 chmod 755 /var/log/guix
563 else
564 if getent group guixbuild > /dev/null; then
565 _msg_info "group guixbuild exists"
566 else
567 groupadd --system guixbuild
568 _msg_pass "group <guixbuild> created"
569 fi
570
571 for i in $(seq -w 1 10); do
572 create_account "guixbuilder${i}" "guixbuild" \
573 "guixbuild${KVMGROUP}" \
574 "Guix build user $i"
575 done
576 fi
577 }
578
579 sys_delete_build_user()
580 {
581 for i in $(seq -w 1 10); do
582 if id -u "guixbuilder${i}" &>/dev/null; then
583 userdel -f guixbuilder"$i"
584 fi
585 done
586
587 _msg_info "delete group guixbuild"
588 if getent group guixbuild &>/dev/null; then
589 groupdel -f guixbuild
590 fi
591
592 _msg_info "remove guix-daemon user"
593 if id guix-daemon &>/dev/null; then
594 userdel -f guix-daemon
595 fi
596 if getent group guix-daemon &>/dev/null; then
597 groupdel -f guix-daemon
598 fi
599 }
600
601 sys_enable_guix_daemon()
602 { # Run the daemon, and set it to automatically start on boot.
603
604 local info_path
605 local local_bin
606 local var_guix
607
608 _debug "--- [ ${FUNCNAME[0]} ] ---"
609
610 info_path="/usr/local/share/info"
611 local_bin="/usr/local/bin"
612 var_guix="/var/guix/profiles/per-user/root/current-guix"
613
614 case "$INIT_SYS" in
615 upstart)
616 { initctl reload-configuration;
617 cp ~root/.config/guix/current/lib/upstart/system/guix-daemon.conf \
618 /etc/init/ &&
619 configure_substitute_discovery /etc/init/guix-daemon.conf &&
620 start guix-daemon; } &&
621 _msg_pass "enabled Guix daemon via upstart"
622 ;;
623 systemd)
624 { install_unit()
625 {
626 local dest="/etc/systemd/system/$1"
627 rm -f "$dest"
628 cp ~root/.config/guix/current/lib/systemd/system/"$1" "$dest"
629 chmod 664 "$dest"
630 systemctl daemon-reload
631 systemctl enable "$1"
632 }
633
634 install_unit guix-daemon.service
635
636 configure_substitute_discovery \
637 /etc/systemd/system/guix-daemon.service
638
639 # Install after guix-daemon.service to avoid a harmless warning.
640 # systemd .mount units must be named after the target directory.
641 install_unit gnu-store.mount
642
643 systemctl daemon-reload &&
644 systemctl start guix-daemon &&
645 systemctl start gnu-store.mount; } &&
646 _msg_pass "enabled Guix daemon via systemd"
647 ;;
648 sysv-init)
649 { mkdir -p /etc/init.d;
650 cp ~root/.config/guix/current/etc/init.d/guix-daemon \
651 /etc/init.d/guix-daemon;
652 chmod 775 /etc/init.d/guix-daemon;
653
654 configure_substitute_discovery /etc/init.d/guix-daemon
655
656 update-rc.d guix-daemon defaults &&
657 update-rc.d guix-daemon enable &&
658 service guix-daemon start; } &&
659 _msg_pass "enabled Guix daemon via sysv"
660 ;;
661 openrc)
662 { mkdir -p /etc/init.d;
663 cp ~root/.config/guix/current/etc/openrc/guix-daemon \
664 /etc/init.d/guix-daemon;
665 chmod 775 /etc/init.d/guix-daemon;
666
667 configure_substitute_discovery /etc/init.d/guix-daemon
668
669 rc-update add guix-daemon default &&
670 rc-service guix-daemon start; } &&
671 _msg_pass "enabled Guix daemon via OpenRC"
672 ;;
673 NA|*)
674 _err "unsupported init system; run the daemon manually:"
675 echo " ~root/.config/guix/current/bin/guix-daemon --build-users-group=guixbuild"
676 ;;
677 esac
678
679 _msg_info "making the guix command available to other users"
680
681 [ -e "$local_bin" ] || mkdir -p "$local_bin"
682 ln -sf "${var_guix}/bin/guix" "$local_bin"
683
684 [ -e "$info_path" ] || mkdir -p "$info_path"
685 for i in "${var_guix}"/share/info/*; do
686 ln -sf "$i" "$info_path"
687 done
688 }
689
690 sys_delete_guix_daemon()
691 { # Disabled, stop and remove the various guix daemons.
692
693 local info_path
694 local local_bin
695 local var_guix
696
697 _debug "--- [ ${FUNCNAME[0]} ] ---"
698
699 info_path="/usr/local/share/info"
700 local_bin="/usr/local/bin"
701
702
703 case "$INIT_SYS" in
704 upstart)
705 _msg_info "stopping guix-daemon"
706 stop guix-daemon
707 _msg_info "removing guix-daemon"
708 rm /etc/init/guix-daemon.conf
709 ;;
710
711 systemd)
712 if [ -f /etc/systemd/system/guix-daemon.service ]; then
713 _msg_info "disabling guix-daemon"
714 systemctl disable guix-daemon
715 _msg_info "stopping guix-daemon"
716 systemctl stop guix-daemon
717 _msg_info "removing guix-daemon"
718 rm -f /etc/systemd/system/guix-daemon.service
719 fi
720
721 if [ -f /etc/systemd/system/gnu-store.mount ]; then
722 _msg_info "disabling gnu-store.mount"
723 systemctl disable gnu-store.mount
724 _msg_info "stopping gnu-store.mount"
725 systemctl stop gnu-store.mount
726 _msg_info "removing gnu-store.mount"
727 rm -f /etc/systemd/system/gnu-store.mount
728 fi
729 systemctl daemon-reload
730 ;;
731
732 sysv-init)
733 update-rc.d guix-daemon disable
734 service guix-daemon stop
735 rm -rf /etc/init.d/guix-daemon
736 ;;
737 NA|*)
738 _err "unsupported init system; disable, stop and remove the daemon manually:"
739 echo " ~root/.config/guix/current/bin/guix-daemon --build-users-group=guixbuild"
740 ;;
741 esac
742
743
744 _msg_info "removing $local_bin/guix"
745 rm -f "$local_bin"/guix
746
747 _msg_info "removing $info_path/guix*"
748 rm -f "$info_path"/guix*
749 }
750
751 sys_authorize_build_farms()
752 { # authorize the public key(s) of the build farm(s)
753 local hosts=(
754 bordeaux.guix.gnu.org
755 ci.guix.gnu.org
756 )
757
758 if prompt_yes_no "Permit downloading pre-built package binaries from the \
759 project's build farms?"; then
760 for host in "${hosts[@]}"; do
761 local key=~root/.config/guix/current/share/guix/$host.pub
762 [ -f "$key" ] \
763 && guix archive --authorize < "$key" \
764 && _msg_pass "Authorized public key for $host"
765 done
766 if id guix-daemon &>/dev/null; then
767 # /etc/guix/acl must be readable by the unprivileged guix-daemon.
768 chown -R guix-daemon:guix-daemon /etc/guix
769 fi
770 else
771 _msg_info "Skipped authorizing build farm public keys"
772 fi
773 }
774
775 sys_create_init_profile()
776 { # Define for better desktop integration
777 # This will not take effect until the next shell or desktop session!
778 [ -d "/etc/profile.d" ] || mkdir /etc/profile.d # Just in case
779 cat <<"EOF" > /etc/profile.d/zzz-guix.sh
780 # Explicitly initialize XDG base directory variables to ease compatibility
781 # with Guix System: see <https://issues.guix.gnu.org/56050#3>.
782 export XCURSOR_PATH="${XCURSOR_PATH:-$HOME/.local/share/icons:$HOME/.icons:/usr/local/share/icons:/usr/share/icons}"
783 export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
784 export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
785 export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
786 export XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}"
787 export XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}"
788 export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
789 # no default for XDG_RUNTIME_DIR (depends on foreign distro for semantics)
790
791 # `guix pull` profile
792 GUIX_PROFILE="$HOME/.config/guix/current"
793 export PATH="$GUIX_PROFILE/bin${PATH:+:}$PATH"
794 # Add to INFOPATH and MANPATH so the latest Guix documentation is available to
795 # info and man readers. When INFOPATH is unset, add a trailing colon so Emacs
796 # searches 'Info-default-directory-list'. When MANPATH is unset, add a
797 # trailing colon so the system default search path is used.
798 export INFOPATH="$GUIX_PROFILE/share/info:${INFOPATH:-}"
799 export MANPATH="$GUIX_PROFILE/share/man:${MANPATH:-}"
800 # Expose the latest Guix modules to Guile so guix shell and repls spawned by
801 # e.g. Geiser work out of the box.
802 export GUILE_LOAD_PATH="$GUIX_PROFILE/share/guile/site/3.0${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}"
803 export GUILE_LOAD_COMPILED_PATH="$GUIX_PROFILE/lib/guile/3.0/site-ccache${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}"
804
805 # User's default profile, if it exists
806 GUIX_PROFILE="$HOME/.guix-profile"
807 if [ -f "$GUIX_PROFILE/etc/profile" ]; then
808 . "$GUIX_PROFILE/etc/profile"
809
810 # see info '(guix) Application Setup'
811 export GUIX_LOCPATH="$GUIX_PROFILE/lib/locale${GUIX_LOCPATH:+:}$GUIX_LOCPATH"
812
813 # Documentation search paths may be handled by $GUIX_PROFILE/etc/profile if
814 # the user installs info and man readers via Guix. If the user doesn’t,
815 # explicitly add to them so documentation for software from ‘guix install’
816 # is available to the system info and man readers.
817 case $INFOPATH in
818 *$GUIX_PROFILE/share/info*) ;;
819 *) export INFOPATH="$GUIX_PROFILE/share/info:$INFOPATH" ;;
820 esac
821 case $MANPATH in
822 *$GUIX_PROFILE/share/man*) ;;
823 *) export MANPATH="$GUIX_PROFILE/share/man:$MANPATH"
824 esac
825
826 case $XDG_DATA_DIRS in
827 *$GUIX_PROFILE/share*) ;;
828 *) export XDG_DATA_DIRS="$GUIX_PROFILE/share:$XDG_DATA_DIRS"
829 esac
830 fi
831
832 # NOTE: Guix Home handles its own profile initialization in ~/.profile. See
833 # info '(guix) Configuring the Shell'.
834
835 # Clean up after ourselves.
836 unset GUIX_PROFILE
837 EOF
838 }
839
840 sys_create_shell_completion()
841 { # Symlink supported shell completions system-wide
842
843 var_guix=/var/guix/profiles/per-user/root/current-guix
844 bash_completion=/etc/bash_completion.d
845 zsh_completion=/usr/share/zsh/site-functions
846 fish_completion=/usr/share/fish/vendor_completions.d
847
848 { # Just in case
849 for dir_shell in $bash_completion $zsh_completion $fish_completion; do
850 [ -d "$dir_shell" ] || mkdir -p "$dir_shell"
851 done;
852
853 # Don't use globing here as we also need to delete the files when
854 # uninstalling Guix
855 ln -sf ${var_guix}/etc/bash_completion.d/guix "$bash_completion";
856 ln -sf ${var_guix}/etc/bash_completion.d/guix-daemon "$bash_completion";
857 ln -sf ${var_guix}/share/zsh/site-functions/_guix "$zsh_completion";
858 ln -sf ${var_guix}/share/fish/vendor_completions.d/guix.fish "$fish_completion"; } &&
859 _msg_pass "installed shell completion"
860 }
861
862 sys_delete_shell_completion()
863 { # Symlink supported shell completions system-wide
864
865 var_guix=/var/guix/profiles/per-user/root/current-guix
866 bash_completion=/etc/bash_completion.d
867 zsh_completion=/usr/share/zsh/site-functions
868 fish_completion=/usr/share/fish/vendor_completions.d
869
870 _msg_info "removing shell completion"
871
872 rm -f "$bash_completion"/guix;
873 rm -f "$bash_completion"/guix-daemon;
874 rm -f "$zsh_completion"/_guix;
875 rm -f "$fish_completion"/guix.fish;
876 }
877
878 sys_customize_bashrc()
879 {
880 prompt_yes_no "Customize users Bash shell prompt for Guix?" || return 0
881
882 for bashrc in /home/*/.bashrc /root/.bashrc; do
883 test -f "$bashrc" || continue
884 # shellcheck disable=SC2016 # intended search for variable reference
885 grep -Fq '$GUIX_ENVIRONMENT' "$bashrc" && continue
886 cp "${bashrc}" "${bashrc}.bak"
887 # shellcheck disable=SC2016,SC2028 # intended literal shell output
888 echo '
889 # Automatically added by the Guix install script.
890 if [ -n "$GUIX_ENVIRONMENT" ]; then
891 if [[ $PS1 =~ (.*)"\\$" ]]; then
892 PS1="${BASH_REMATCH[1]} [env]\\\$ "
893 fi
894 fi
895 ' >> "$bashrc"
896 done
897 _msg_pass "Bash shell prompt successfully customized for Guix"
898 }
899
900 sys_maybe_setup_selinux()
901 {
902 if ! [ -f /sys/fs/selinux/policy ]
903 then
904 return
905 fi
906
907 local c
908 for c in semodule restorecon
909 do
910 if ! command -v "$c" &>/dev/null
911 then
912 return
913 fi
914 done
915
916 prompt_yes_no "Install SELinux policy that might be required to run guix-daemon?" \
917 || return 0
918
919 local var_guix=/var/guix/profiles/per-user/root/current-guix
920 semodule -i "${var_guix}/share/selinux/guix-daemon.cil"
921 restorecon -R /gnu /var/guix
922 }
923
924 sys_maybe_setup_apparmor()
925 {
926 if ! [ -f /sys/module/apparmor/parameters/enabled ]
927 then
928 return
929 fi
930
931 if ! command -v apparmor_parser &> /dev/null
932 then
933 return
934 fi
935
936 prompt_yes_no "Install AppArmor profile that might be required to run guix-daemon?" \
937 || return 0
938
939 local var_guix=/var/guix/profiles/per-user/root/current-guix
940 local apparmor_sources="${var_guix}/etc/apparmor.d/"
941
942 if ! [ -d "$apparmor_sources" ]
943 then
944 _err "This version of Guix doesn’t contain AppArmor profiles."
945 return
946 fi
947
948 cp -f -t /etc/apparmor.d/tunables "$apparmor_sources/tunables/guix"
949 cp -f -t /etc/apparmor.d "$apparmor_sources/guix-daemon"
950 cp -f -t /etc/apparmor.d "$apparmor_sources/guix"
951 apparmor_parser --warn=all -r /etc/apparmor.d/guix-daemon
952 apparmor_parser --warn=all -r /etc/apparmor.d/guix
953
954 _msg_pass "apparmor profiles installed and loaded"
955 }
956
957 sys_delete_apparmor_profiles()
958 {
959 # Not a big deal if the apparmor_parser commands fail as they only apply
960 # for the current boot, we still want to go on and remove the files.
961 if [ -f "/etc/apparmor.d/guix" ]
962 then
963 _msg_info "removing /etc/apparmor.d/guix"
964 apparmor_parser -R /etc/apparmor.d/guix || true
965 rm -f "/etc/apparmor.d/guix"
966 fi
967
968 if [ -f "/etc/apparmor.d/guix-daemon" ]
969 then
970 _msg_info "removing /etc/apparmor.d/guix-daemon"
971 apparmor_parser -R /etc/apparmor.d/guix-daemon || true
972 rm -f "/etc/apparmor.d/guix-daemon"
973 fi
974
975 if [ -f "/etc/apparmor.d/tunables/guix" ]
976 then
977 _msg_info "removing /etc/apparmor.d/tunables/guix"
978 rm -f "/etc/apparmor.d/tunables/guix"
979 fi
980 }
981
982 sys_delete_init_profile()
983 {
984 _msg_info "removing /etc/profile.d/zzz-guix.sh"
985 rm -f /etc/profile.d/zzz-guix.sh
986 }
987
988 sys_delete_user_profiles()
989 {
990 _msg_info "removing ~root/.guix-profile"
991 rm -f ~root/.guix-profile
992 rm -rf ~root/.cache/guix
993
994 _msg_info "removing .guix-profile, .cache/guix and .config/guix of all /home users"
995 rm -rf /home/*/{.guix-profile,{.cache,.config}/guix}
996 }
997
998 welcome()
999 {
1000 local uninstall_flag="$1"
1001 local char
1002 cat<<"EOF"
1003 ��������� ���������
1004 ��������������������������������������� ���������������������������������������
1005 ������������������������������������������ ���������������������������������������
1006 ��������������������������������� ������������������������������
1007 ������������������ ������������������
1008 ��������������� ������������������
1009 ��������������� ���������������
1010 ������������������ ���������������
1011 ��������������� ���������������
1012 ��������������� ���������������
1013 ���������������������������������
1014 ���������������������������
1015 ���������������������
1016 _____ _ _ _ _ _____ _
1017 / ____| \ | | | | | / ____| (_)
1018 | | __| \| | | | | | | __ _ _ ___ __
1019 | | |_ | . ' | | | | | | |_ | | | | \ \/ /
1020 | |__| | |\ | |__| | | |__| | |_| | |> <
1021 \_____|_| \_|\____/ \_____|\__,_|_/_/\_\
1022
1023 https://www.gnu.org/software/guix/
1024 EOF
1025
1026 if [ '--uninstall' = "$uninstall_flag" ]; then
1027 echo "${WARN}This script will uninstall GNU Guix from your system"
1028 echo "To install, run this script with no parameters."
1029 else
1030 echo "This script installs GNU Guix on your system"
1031 echo "To uninstall, pass in the '--uninstall' parameter."
1032 fi
1033
1034 # Don't use ���read -p��� here! It won't display when run non-interactively.
1035 echo -n "Press return to continue..."$'\r'
1036 if ! read -r char; then
1037 echo
1038 die "Can't read standard input. Hint: don't pipe scripts into a shell."
1039 fi
1040 if [ "$char" ]; then
1041 echo
1042 echo "...that ($char) was not a return!"
1043 _msg_warn "Use newlines to automate installation, e.g.: yes '' | ${0##*/}"
1044 _msg_warn "Any other method is unsupported and likely to break in future."
1045 fi
1046 }
1047
1048 main_install()
1049 {
1050 local tmp_path
1051 welcome
1052
1053 _msg "Starting installation ($(date))"
1054
1055 chk_term
1056 chk_init_sys
1057 add_init_sys_require
1058 chk_require "${REQUIRE[@]}"
1059 chk_sys_arch
1060 chk_sys_nscd
1061 chk_existing
1062
1063 _msg_info "system is ${ARCH_OS}"
1064
1065 umask 0022
1066 tmp_path="$(mktemp -t -d guix.XXXXXX)"
1067
1068 if [ -z "${GUIX_BINARY_FILE_NAME}" ]; then
1069 chk_gpg_keyring
1070 guix_get_bin_list "${GNU_URL}"
1071 guix_get_bin "${GNU_URL}" "${BIN_VER}" "$tmp_path"
1072 GUIX_BINARY_FILE_NAME=${BIN_VER}.tar.xz
1073 else
1074 if ! [[ $GUIX_BINARY_FILE_NAME =~ $ARCH_OS ]]; then
1075 _err "$ARCH_OS not in ${GUIX_BINARY_FILE_NAME}; aborting"
1076 fi
1077 _msg_info "Using manually provided binary ${GUIX_BINARY_FILE_NAME}"
1078 GUIX_BINARY_FILE_NAME=$(realpath "$GUIX_BINARY_FILE_NAME")
1079 fi
1080
1081 sys_create_store "${GUIX_BINARY_FILE_NAME}" "${tmp_path}"
1082 sys_create_build_user
1083 sys_maybe_setup_selinux
1084 sys_maybe_setup_apparmor
1085 sys_enable_guix_daemon
1086 sys_authorize_build_farms
1087 sys_create_init_profile
1088 sys_create_shell_completion
1089 sys_customize_bashrc
1090
1091 _msg_info "cleaning up ${tmp_path}"
1092 rm -r "${tmp_path}"
1093
1094 _msg_pass "Guix has successfully been installed!"
1095 _msg_info "Run 'info guix' to read the manual."
1096
1097 # Required to source /etc/profile in desktop environments.
1098 _msg_info "Please log out and back in to complete the installation."
1099 }
1100
1101 main_uninstall()
1102 {
1103 welcome --uninstall
1104 _msg "Starting uninstall process ($(date))"
1105
1106 chk_term
1107 chk_require "${REQUIRE[@]}"
1108 # it's ok to leave the gpg key
1109 chk_init_sys
1110 chk_sys_arch
1111
1112 _msg_info "system is ${ARCH_OS}"
1113
1114 # stop the build, package system.
1115 sys_delete_guix_daemon
1116 # stop people from accessing their profiles.
1117 sys_delete_user_profiles
1118 sys_delete_apparmor_profiles
1119 # kill guix off all the guts of guix
1120 sys_delete_store
1121 # clean up the system
1122 sys_delete_init_profile
1123 sys_delete_build_user
1124 sys_delete_shell_completion
1125
1126 # these directories are created on the fly during usage.
1127 _msg_info "removing /etc/guix"
1128 rm -rf /etc/guix
1129 _msg_info "removing /var/log/guix"
1130 rm -rf /var/log/guix
1131
1132 _msg_pass "Guix has successfully been uninstalled!"
1133 }
1134
1135 main()
1136 {
1137 # expect no parameters
1138 # or '--uninstall'
1139 if [ 0 -eq $# ]; then
1140 main_install
1141 else
1142 local uninstall_flag="$1"
1143 if [ '--uninstall' = "${uninstall_flag}" ]; then
1144 main_uninstall
1145 else
1146 echo "unsupported parameters: $*"
1147 exit 1
1148 fi
1149 fi
1150 }
1151
1152 main "$@"