Compare commits
3 Commits
master
...
wip/sysroo
Author | SHA1 | Date | |
---|---|---|---|
|
db0b093076 | ||
|
8f7fc3f5d7 | ||
|
e418a17468 |
@ -1,26 +0,0 @@
|
||||
# EditorConfig
|
||||
# https://editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file, utf-8 charset, 2 space
|
||||
# indentation, remove any whitespace characters preceding newline characters.
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
# for shfmt
|
||||
switch_case_indent = true
|
||||
binary_next_line = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,5 +2,4 @@
|
||||
arch-chroot
|
||||
genfstab
|
||||
pacstrap
|
||||
doc/*.8
|
||||
arch-install-scripts-*.tar.gz*
|
||||
|
@ -1,27 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
stages:
|
||||
- check
|
||||
|
||||
.pacman_install:
|
||||
before_script:
|
||||
# NOTE: Install latest archlinux-keyring before upgrading system. In the
|
||||
# future this should not be needed anymore when we can guarantee a valid
|
||||
# keyring for longer:
|
||||
# https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/issues/4
|
||||
- echo -e "\e[0Ksection_start:`date +%s`:pacman[collapsed=true]\r\e[0KInstalling dependencies"
|
||||
- pacman -Sy --needed --noconfirm archlinux-keyring
|
||||
- pacman --noconfirm -Syu --needed asciidoc make shellcheck m4
|
||||
- echo -e "\e[0Ksection_end:`date +%s`:pacman\r\e[0K"
|
||||
|
||||
check:
|
||||
stage: check
|
||||
extends: .pacman_install
|
||||
script:
|
||||
- make shellcheck
|
||||
|
||||
test:
|
||||
stage: check
|
||||
extends: .pacman_install
|
||||
script:
|
||||
- make check
|
54
Makefile
54
Makefile
@ -1,23 +1,15 @@
|
||||
VER=26
|
||||
VER=18
|
||||
|
||||
PREFIX = /usr
|
||||
PREFIX = /usr/local
|
||||
|
||||
BINPROGS = \
|
||||
future-chroot \
|
||||
arch-chroot \
|
||||
genfstab \
|
||||
pacstrap
|
||||
|
||||
MANS = \
|
||||
doc/future-chroot.8 \
|
||||
doc/genfstab.8 \
|
||||
doc/pacstrap.8
|
||||
|
||||
BASH = bash
|
||||
ZSHCOMP := $(wildcard completion/zsh/*)
|
||||
BASHCOMP := $(wildcard completion/bash/*)
|
||||
|
||||
all: $(BINPROGS) man
|
||||
man: $(MANS)
|
||||
all: $(BINPROGS)
|
||||
|
||||
V_GEN = $(_v_GEN_$(V))
|
||||
_v_GEN_ = $(_v_GEN_0)
|
||||
@ -25,37 +17,27 @@ _v_GEN_0 = @echo " GEN " $@;
|
||||
|
||||
edit = $(V_GEN) m4 -P $@.in >$@ && chmod go-w,+x $@
|
||||
|
||||
future-chroot: future-chroot.in common
|
||||
%: %.in common
|
||||
$(edit)
|
||||
|
||||
genfstab: genfstab.in fstab-helpers
|
||||
$(edit)
|
||||
|
||||
pacstrap: pacstrap.in common
|
||||
$(edit)
|
||||
|
||||
doc/%: doc/%.asciidoc doc/asciidoc.conf
|
||||
$(V_GEN) a2x --no-xmllint --asciidoc-opts="-f doc/asciidoc.conf" -d manpage -f manpage -D doc $<
|
||||
|
||||
clean:
|
||||
$(RM) $(BINPROGS) $(MANS)
|
||||
$(RM) $(BINPROGS)
|
||||
|
||||
check: all
|
||||
@for f in $(BINPROGS); do bash -O extglob -n $$f; done
|
||||
@r=0; for t in test/test_*; do $(BASH) $$t || { echo $$t fail; r=1; }; done; exit $$r
|
||||
|
||||
shellcheck: $(BINPROGS)
|
||||
shellcheck -W 99 --color $(BINPROGS)
|
||||
shellcheck -W 99 --color -x test/test_*
|
||||
|
||||
install: all
|
||||
install -d $(DESTDIR)$(PREFIX)/sbin
|
||||
install -m 0755 $(BINPROGS) $(DESTDIR)$(PREFIX)/sbin
|
||||
install -d $(DESTDIR)$(PREFIX)/share/zsh/site-functions
|
||||
install -m 0644 $(ZSHCOMP) $(DESTDIR)$(PREFIX)/share/zsh/site-functions
|
||||
install -d $(DESTDIR)$(PREFIX)/share/bash-completion/completions
|
||||
install -m 0644 $(BASHCOMP) $(DESTDIR)$(PREFIX)/share/bash-completion/completions
|
||||
install -d $(DESTDIR)$(PREFIX)/share/man/man8
|
||||
install -m 0644 $(MANS) $(DESTDIR)$(PREFIX)/share/man/man8
|
||||
install -dm755 $(DESTDIR)$(PREFIX)/bin
|
||||
install -m755 $(BINPROGS) $(DESTDIR)$(PREFIX)/bin
|
||||
install -Dm644 zsh-completion $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_archinstallscripts
|
||||
|
||||
.PHONY: all man clean check shellcheck install
|
||||
uninstall:
|
||||
for f in $(BINPROGS); do $(RM) $(DESTDIR)$(PREFIX)/bin/$$f; done
|
||||
$(RM) $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_archinstallscripts
|
||||
|
||||
dist:
|
||||
git archive --format=tar --prefix=arch-install-scripts-$(VER)/ v$(VER) | gzip -9 > arch-install-scripts-$(VER).tar.gz
|
||||
gpg --detach-sign --use-agent arch-install-scripts-$(VER).tar.gz
|
||||
|
||||
.PHONY: all clean install uninstall dist
|
||||
|
@ -1,17 +1,14 @@
|
||||
# Modify Arch Install Scripts
|
||||
|
||||
# Future Install Scripts
|
||||
# Arch Install Scripts
|
||||
|
||||
This is a small suite of scripts aimed at automating some menial
|
||||
tasks when installing [Future Linux](https://www.futurelinux.xyz).
|
||||
tasks when installing [Arch Linux](https://www.archlinux.org).
|
||||
|
||||
## Requirements
|
||||
|
||||
* GNU coreutils (>= v8.15)
|
||||
* util-linux (>= 2.39)
|
||||
* util-linux (>= 2.23)
|
||||
* POSIX awk
|
||||
* bash (>= 4.1)
|
||||
* asciidoc (for generating man pages)
|
||||
|
||||
## License
|
||||
|
||||
|
79
arch-chroot.in
Normal file
79
arch-chroot.in
Normal file
@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
m4_include(common)
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: ${0##*/} chroot-dir [command]
|
||||
|
||||
-h Print this help message
|
||||
-u <user>[:group] Specify non-root user and optional group to use
|
||||
|
||||
If 'command' is unspecified, ${0##*/} will launch /bin/bash.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
chroot_add_resolv_conf() {
|
||||
local chrootdir=$1 resolv_conf=$1/etc/resolv.conf
|
||||
|
||||
[[ -e /etc/resolv.conf ]] || return 0
|
||||
|
||||
# Handle resolv.conf as a symlink to somewhere else.
|
||||
if [[ -L $chrootdir/etc/resolv.conf ]]; then
|
||||
# readlink(1) should always give us *something* since we know at this point
|
||||
# it's a symlink. For simplicity, ignore the case of nested symlinks.
|
||||
resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
|
||||
if [[ $resolv_conf = /* ]]; then
|
||||
resolv_conf=$chrootdir$resolv_conf
|
||||
else
|
||||
resolv_conf=$chrootdir/etc/$resolv_conf
|
||||
fi
|
||||
|
||||
# ensure file exists to bind mount over
|
||||
if [[ ! -f $resolv_conf ]]; then
|
||||
install -Dm644 /dev/null "$resolv_conf" || return 1
|
||||
fi
|
||||
elif [[ ! -e $chrootdir/etc/resolv.conf ]]; then
|
||||
# The chroot might not have a resolv.conf.
|
||||
return 0
|
||||
fi
|
||||
|
||||
chroot_add_mount /etc/resolv.conf "$resolv_conf" --bind
|
||||
}
|
||||
|
||||
while getopts ':hu:' flag; do
|
||||
case $flag in
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
u)
|
||||
userspec=$OPTARG
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
?)
|
||||
die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
(( $# )) || die 'No chroot directory specified'
|
||||
chrootdir=$1
|
||||
shift
|
||||
|
||||
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
|
||||
|
||||
chroot_setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
|
||||
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
|
||||
|
||||
chroot_args=()
|
||||
[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
|
||||
|
||||
SHELL=/bin/bash unshare --fork --pid chroot "${chroot_args[@]}" -- "$chrootdir" "$@"
|
269
common
269
common
@ -1,11 +1,53 @@
|
||||
#!/hint/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# shellcheck disable=SC2059 # $1 and $2 can contain the printf modifiers
|
||||
# generated from util-linux source: libmount/src/utils.c
|
||||
declare -A pseudofs_types=([anon_inodefs]=1
|
||||
[autofs]=1
|
||||
[bdev]=1
|
||||
[binfmt_misc]=1
|
||||
[cgroup]=1
|
||||
[cgroup2]=1
|
||||
[configfs]=1
|
||||
[cpuset]=1
|
||||
[debugfs]=1
|
||||
[devfs]=1
|
||||
[devpts]=1
|
||||
[devtmpfs]=1
|
||||
[dlmfs]=1
|
||||
[fuse.gvfs-fuse-daemon]=1
|
||||
[fusectl]=1
|
||||
[hugetlbfs]=1
|
||||
[mqueue]=1
|
||||
[nfsd]=1
|
||||
[none]=1
|
||||
[pipefs]=1
|
||||
[proc]=1
|
||||
[pstore]=1
|
||||
[ramfs]=1
|
||||
[rootfs]=1
|
||||
[rpc_pipefs]=1
|
||||
[securityfs]=1
|
||||
[sockfs]=1
|
||||
[spufs]=1
|
||||
[sysfs]=1
|
||||
[tmpfs]=1)
|
||||
|
||||
# generated from: pkgfile -vbr '/fsck\..+' | awk -F. '{ print $NF }' | sort
|
||||
declare -A fsck_types=([cramfs]=1
|
||||
[exfat]=1
|
||||
[ext2]=1
|
||||
[ext3]=1
|
||||
[ext4]=1
|
||||
[ext4dev]=1
|
||||
[jfs]=1
|
||||
[minix]=1
|
||||
[msdos]=1
|
||||
[reiserfs]=1
|
||||
[vfat]=1
|
||||
[xfs]=1)
|
||||
|
||||
out() { printf "$1 $2\n" "${@:3}"; }
|
||||
error() { out "==> ERROR:" "$@"; } >&2
|
||||
warning() { out "==> WARNING:" "$@"; } >&2
|
||||
msg() { out "==>" "$@"; }
|
||||
msg2() { out " ->" "$@";}
|
||||
die() { error "$@"; exit 1; }
|
||||
|
||||
ignore_error() {
|
||||
@ -13,6 +55,14 @@ ignore_error() {
|
||||
return 0
|
||||
}
|
||||
|
||||
in_array() {
|
||||
local i
|
||||
for i in "${@:2}"; do
|
||||
[[ $1 = "$i" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
chroot_add_mount() {
|
||||
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
|
||||
}
|
||||
@ -29,90 +79,175 @@ chroot_setup() {
|
||||
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
|
||||
trap 'chroot_teardown' EXIT
|
||||
|
||||
chroot_add_mount --bind /dev "$1/dev" &&
|
||||
chroot_add_mount devpts "$1/dev/pts" -t devpts -o gid=5,mode=0620 &&
|
||||
chroot_add_mount proc $1/proc -t proc &&
|
||||
chroot_add_mount sysfs $1/sys -t sysfs &&
|
||||
chroot_add_mount tmpfs "$1/run" -t tmpfs &&
|
||||
chroot_add_mount tmpfs "$1/dev/shm" -t tmpfs -o nosuid,nodev &&
|
||||
chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
|
||||
chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
|
||||
ignore_error chroot_maybe_add_mount "[[ -d '$1/sys/firmware/efi/efivars' ]]" \
|
||||
efivarfs "$1/sys/firmware/efi/efivars" -t efivarfs
|
||||
|
||||
efivarfs "$1/sys/firmware/efi/efivars" -t efivarfs -o nosuid,noexec,nodev &&
|
||||
chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
|
||||
chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
|
||||
chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
|
||||
chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
|
||||
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
|
||||
chroot_maybe_add_mount "(( hostcache ))" /var/cache/pacman/pkg "$1/var/cache/pacman/pkg" -o bind
|
||||
}
|
||||
|
||||
chroot_teardown() {
|
||||
if (( ${#CHROOT_ACTIVE_MOUNTS[@]} )); then
|
||||
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
|
||||
fi
|
||||
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
|
||||
unset CHROOT_ACTIVE_MOUNTS
|
||||
}
|
||||
|
||||
chroot_add_mount_lazy() {
|
||||
mount "$@" && CHROOT_ACTIVE_LAZY=("$2" "${CHROOT_ACTIVE_LAZY[@]}")
|
||||
try_cast() (
|
||||
_=$(( $1#$2 ))
|
||||
) 2>/dev/null
|
||||
|
||||
valid_number_of_base() {
|
||||
local base=$1 len=${#2} i=
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
try_cast "$base" "${2:i:1}" || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
chroot_bind_device() {
|
||||
touch "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
|
||||
chroot_add_mount "$1" "$2" --bind
|
||||
mangle() {
|
||||
local i= chr= out=
|
||||
local {a..f}= {A..F}=
|
||||
|
||||
for (( i = 0; i < ${#1}; i++ )); do
|
||||
chr=${1:i:1}
|
||||
case $chr in
|
||||
[[:space:]\\])
|
||||
printf -v chr '%03o' "'$chr"
|
||||
out+=\\
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
done
|
||||
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
chroot_add_link() {
|
||||
ln -sf "$1" "$2" && CHROOT_ACTIVE_FILES=("$2" "${CHROOT_ACTIVE_FILES[@]}")
|
||||
unmangle() {
|
||||
local i= chr= out= len=$(( ${#1} - 4 ))
|
||||
local {a..f}= {A..F}=
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
chr=${1:i:1}
|
||||
case $chr in
|
||||
\\)
|
||||
if valid_number_of_base 8 "${1:i+1:3}" ||
|
||||
valid_number_of_base 16 "${1:i+1:3}"; then
|
||||
printf -v chr '%b' "${1:i:4}"
|
||||
(( i += 3 ))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
done
|
||||
|
||||
printf '%s' "$out${1:i}"
|
||||
}
|
||||
|
||||
unshare_setup() {
|
||||
CHROOT_ACTIVE_MOUNTS=()
|
||||
CHROOT_ACTIVE_LAZY=()
|
||||
CHROOT_ACTIVE_FILES=()
|
||||
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
|
||||
trap 'unshare_teardown' EXIT
|
||||
optstring_match_option() {
|
||||
local candidate pat patterns
|
||||
|
||||
chroot_add_mount_lazy "$1" "$1" --bind &&
|
||||
chroot_add_mount proc "$1/proc" -t proc &&
|
||||
chroot_add_mount sysfs "$1/sys" -t sysfs &&
|
||||
chroot_add_link /proc/self/fd "$1/dev/fd" &&
|
||||
chroot_add_link /proc/self/fd/0 "$1/dev/stdin" &&
|
||||
chroot_add_link /proc/self/fd/1 "$1/dev/stdout" &&
|
||||
chroot_add_link /proc/self/fd/2 "$1/dev/stderr" &&
|
||||
chroot_bind_device /dev/full "$1/dev/full" &&
|
||||
chroot_bind_device /dev/null "$1/dev/null" &&
|
||||
chroot_bind_device /dev/random "$1/dev/random" &&
|
||||
chroot_bind_device /dev/tty "$1/dev/tty" &&
|
||||
chroot_bind_device /dev/urandom "$1/dev/urandom" &&
|
||||
chroot_bind_device /dev/zero "$1/dev/zero" &&
|
||||
chroot_add_mount tmpfs "$1/run" -t tmpfs
|
||||
IFS=, read -ra patterns <<<"$1"
|
||||
for pat in "${patterns[@]}"; do
|
||||
if [[ $pat = *=* ]]; then
|
||||
# "key=val" will only ever match "key=val"
|
||||
candidate=$2
|
||||
else
|
||||
# "key" will match "key", but also "key=anyval"
|
||||
candidate=${2%%=*}
|
||||
fi
|
||||
|
||||
[[ $pat = "$candidate" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
unshare_teardown() {
|
||||
chroot_teardown
|
||||
optstring_remove_option() {
|
||||
local o options_ remove=$2 IFS=,
|
||||
|
||||
if (( ${#CHROOT_ACTIVE_LAZY[@]} )); then
|
||||
umount --lazy "${CHROOT_ACTIVE_LAZY[@]}"
|
||||
read -ra options_ <<<"${!1}"
|
||||
|
||||
for o in "${!options_[@]}"; do
|
||||
optstring_match_option "$remove" "${options_[o]}" && unset 'options_[o]'
|
||||
done
|
||||
|
||||
declare -g "$1=${options_[*]}"
|
||||
}
|
||||
|
||||
optstring_normalize() {
|
||||
local o options_ norm IFS=,
|
||||
|
||||
read -ra options_ <<<"${!1}"
|
||||
|
||||
# remove empty fields
|
||||
for o in "${options_[@]}"; do
|
||||
[[ $o ]] && norm+=("$o")
|
||||
done
|
||||
|
||||
# avoid empty strings, reset to "defaults"
|
||||
declare -g "$1=${norm[*]:-defaults}"
|
||||
}
|
||||
|
||||
optstring_append_option() {
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=${!1},$2"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_LAZY
|
||||
|
||||
if (( ${#CHROOT_ACTIVE_FILES[@]} )); then
|
||||
rm "${CHROOT_ACTIVE_FILES[@]}"
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
optstring_prepend_option() {
|
||||
local options_=$1
|
||||
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=$2,${!1}"
|
||||
fi
|
||||
unset CHROOT_ACTIVE_FILES
|
||||
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
pid_unshare="unshare --fork --pid"
|
||||
mount_unshare="$pid_unshare --mount --map-auto --map-root-user --setuid 0 --setgid 0"
|
||||
optstring_get_option() {
|
||||
local opts o
|
||||
|
||||
# This outputs code for declaring all variables to stdout. For example, if
|
||||
# FOO=BAR, then running
|
||||
# declare -p FOO
|
||||
# will result in the output
|
||||
# declare -- FOO="bar"
|
||||
# This function may be used to re-declare all currently used variables and
|
||||
# functions in a new shell.
|
||||
declare_all() {
|
||||
# Remove read-only variables to avoid warnings. Unfortunately, declare +r -p
|
||||
# doesn't work like it looks like it should (declaring only read-write
|
||||
# variables). However, declare -rp will print out read-only variables, which
|
||||
# we can then use to remove those definitions.
|
||||
declare -p | grep -Fvf <(declare -rp)
|
||||
# Then declare functions
|
||||
declare -pf
|
||||
IFS=, read -ra opts <<<"${!1}"
|
||||
for o in "${opts[@]}"; do
|
||||
if optstring_match_option "$2" "$o"; then
|
||||
declare -g "$o"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
optstring_has_option() {
|
||||
local "${2%%=*}"
|
||||
|
||||
optstring_get_option "$1" "$2"
|
||||
}
|
||||
|
||||
dm_name_for_devnode() {
|
||||
read dm_name <"/sys/class/block/${1#/dev/}/dm/name"
|
||||
if [[ $dm_name ]]; then
|
||||
printf '/dev/mapper/%s' "$dm_name"
|
||||
else
|
||||
# don't leave the caller hanging, just print the original name
|
||||
# along with the failure.
|
||||
print '%s' "$1"
|
||||
error 'Failed to resolve device mapper name for: %s' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
fstype_is_pseudofs() {
|
||||
(( pseudofs_types["$1"] ))
|
||||
}
|
||||
|
||||
fstype_has_fsck() {
|
||||
(( fsck_types["$1"] ))
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
_future_chroot() {
|
||||
compopt +o dirnames
|
||||
local cur prev opts i
|
||||
_init_completion -n : || return
|
||||
opts="-N -u -r -h"
|
||||
|
||||
for i in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
|
||||
if [[ -d ${i} ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${prev} = -u ]]; then
|
||||
_usergroup -u
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ${cur} = -* ]]; then
|
||||
COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
|
||||
return 0
|
||||
fi
|
||||
compopt -o dirnames
|
||||
}
|
||||
|
||||
complete -F _future_chroot future-chroot
|
@ -1,31 +0,0 @@
|
||||
_genfstab() {
|
||||
compopt -o dirnames
|
||||
local cur prev words cword
|
||||
_init_completion || return
|
||||
|
||||
local opts="-f -L -p -P -t -U -h"
|
||||
|
||||
case ${prev} in
|
||||
-f)
|
||||
return 0
|
||||
;;
|
||||
-t)
|
||||
COMPREPLY=($(compgen -W "LABEL UUID PARTLABEL PARTUUID" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ ${cur} = -* ]]; then
|
||||
COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
|
||||
return 0
|
||||
fi
|
||||
|
||||
for i in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
|
||||
if [[ -d ${i} ]]; then
|
||||
compopt +o dirnames
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
complete -F _genfstab genfstab
|
@ -1,38 +0,0 @@
|
||||
if ! declare -F _pacman_pkg > /dev/null; then
|
||||
_completion_loader pacman
|
||||
fi
|
||||
|
||||
_pacstrap() {
|
||||
compopt +o dirnames +o default
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
opts="-C -c -D -G -i -K -M -N -P -U -h"
|
||||
|
||||
for i in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
|
||||
if [[ -d ${i} ]]; then
|
||||
_pacman_pkg Slq
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
case ${prev} in
|
||||
-h)
|
||||
return 0
|
||||
;;
|
||||
-C)
|
||||
compopt -o default
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ ${cur} = -* ]]; then
|
||||
COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
|
||||
return 0
|
||||
fi
|
||||
|
||||
compopt -o dirnames
|
||||
}
|
||||
|
||||
complete -F _pacstrap pacstrap
|
@ -1,31 +0,0 @@
|
||||
#compdef future-chroot
|
||||
|
||||
# NOTE: nearly everything here is borrowed from the chroot completion
|
||||
local -a args=(
|
||||
'(-h --help)'{-h,--help}'[display help]'
|
||||
'-N[Run in unshare mode as a regular user]'
|
||||
'-u[The non-root user and optional group to use]: :->userspecs'
|
||||
'-r[Do not change the resolv.conf within the chroot]'
|
||||
'1:new root directory:_directories'
|
||||
'*:::command:_normal'
|
||||
)
|
||||
|
||||
local ret=1
|
||||
|
||||
_arguments $args && ret=0
|
||||
|
||||
# @todo user:group specs are probably used often enough to justify making a type
|
||||
# function for this (see also `chown`, `cpio`, `rsync`, ...)
|
||||
|
||||
[[ $state == userspecs ]] &&
|
||||
if compset -P '*:*:'; then
|
||||
ret=1
|
||||
elif compset -P '*:'; then
|
||||
_groups && ret=0
|
||||
elif compset -S ':*'; then
|
||||
_users && ret=0
|
||||
else
|
||||
_users -qS : && ret=0
|
||||
fi
|
||||
|
||||
return ret
|
@ -1,13 +0,0 @@
|
||||
#compdef genfstab
|
||||
|
||||
local -a args=(
|
||||
'(-h --help)'{-h,--help}'[display help]'
|
||||
'-p[Avoid printing pseudofs mounts]'
|
||||
'-f[Restrict output to mountpoints matching the prefix FILTER]'
|
||||
'(-U -L)-t[Use TAG for source identifiers]:tag:(LABEL UUID PARTLABEL PARTUUID)'
|
||||
'(-U -t)-L[Use labels for source identifiers]'
|
||||
'(-L -t)-U[Use UUIDs for source identifiers]'
|
||||
':*:_path_files -/'
|
||||
)
|
||||
|
||||
_arguments $args
|
@ -1,37 +0,0 @@
|
||||
## linkman: macro
|
||||
# Inspired by/borrowed from the GIT source tree at Documentation/asciidoc.conf
|
||||
#
|
||||
# Usage: linkman:command[manpage-section]
|
||||
#
|
||||
# Note, {0} is the manpage section, while {target} is the command.
|
||||
#
|
||||
# Show man link as: <command>(<section>); if section is defined, else just show
|
||||
# the command.
|
||||
|
||||
[macros]
|
||||
(?su)[\\]?(?P<name>linkman):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
|
||||
|
||||
[attributes]
|
||||
asterisk=*
|
||||
plus=+
|
||||
caret=^
|
||||
startsb=[
|
||||
endsb=]
|
||||
backslash=\
|
||||
tilde=~
|
||||
apostrophe='
|
||||
backtick=`
|
||||
litdd=--
|
||||
|
||||
ifdef::backend-docbook[]
|
||||
[linkman-inlinemacro]
|
||||
{0%{target}}
|
||||
{0#<citerefentry>}
|
||||
{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
|
||||
{0#</citerefentry>}
|
||||
endif::backend-docbook[]
|
||||
|
||||
ifdef::backend-xhtml11[]
|
||||
[linkman-inlinemacro]
|
||||
<a href="{target}.{0}.html">{target}{0?({0})}</a>
|
||||
endif::backend-xhtml11[]
|
@ -1,53 +0,0 @@
|
||||
future-chroot(8)
|
||||
==============
|
||||
|
||||
Name
|
||||
----
|
||||
future-chroot - enhanced chroot command
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
future-chroot [options] chroot-dir [command] [arguments...]
|
||||
|
||||
Description
|
||||
-----------
|
||||
future-chroot wraps the linkman:chroot[1] command while ensuring that important
|
||||
functionality is available, e.g. mounting '/dev/', '/proc' and other API
|
||||
filesystems, or exposing linkman:resolv.conf[5] to the chroot.
|
||||
|
||||
If 'command' is unspecified, future-chroot will launch */bin/bash*.
|
||||
|
||||
[NOTE]
|
||||
======
|
||||
The target chroot-dir *should* be a mountpoint. This ensures that tools such as
|
||||
linkman:pacman[8] or linkman:findmnt[8] have an accurate hierarchy of the
|
||||
mounted filesystems within the chroot. If your chroot target is not a
|
||||
mountpoint, you can bind mount the directory on itself to make it a mountpoint,
|
||||
i.e.:
|
||||
|
||||
'mount --bind /your/chroot /your/chroot'
|
||||
======
|
||||
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
*-N*::
|
||||
Run in unshare mode. This will use linkman:unshare[1] to create a new
|
||||
mount and user namespace, allowing regular users to create new system
|
||||
installations.
|
||||
|
||||
*-u <user>[:group]*::
|
||||
Specify non-root user and optional group to use.
|
||||
|
||||
*-r*::
|
||||
Do not change the resolv.conf within the chroot. This means that the resolver
|
||||
might not work in the chroot, which could be the required state.
|
||||
|
||||
*-h*::
|
||||
Output syntax and command line options.
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
@ -1,48 +0,0 @@
|
||||
genfstab(8)
|
||||
===========
|
||||
|
||||
Name
|
||||
----
|
||||
genfstab - generate output suitable for addition to an fstab file
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
genfstab [options] root
|
||||
|
||||
Description
|
||||
-----------
|
||||
genfstab helps fill in an fstab file by autodetecting all the current mounts
|
||||
below a given mountpoint and printing them in fstab-compatible format to
|
||||
standard output. It can be used to persist a manually mounted filesystem
|
||||
hierarchy and is often used during the initial install and configuration of an
|
||||
OS.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
*-f* <filter>::
|
||||
Restrict output to mountpoints matching the prefix 'filter'.
|
||||
|
||||
*-L*::
|
||||
Use labels for source identifiers (shortcut for '-t LABEL').
|
||||
|
||||
*-p*::
|
||||
Exclude pseudofs mounts (default behavior).
|
||||
|
||||
*-P*::
|
||||
Include pseudofs mounts.
|
||||
|
||||
*-t* <tag>::
|
||||
Use 'tag' for source identifiers (should be one of: 'LABEL', 'UUID',
|
||||
'PARTLABEL', 'PARTUUID').
|
||||
|
||||
*-U*::
|
||||
Use UUIDs for source identifiers (shortcut for '-t UUID').
|
||||
|
||||
*-h*::
|
||||
Output syntax and command line options.
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
@ -1,64 +0,0 @@
|
||||
pacstrap(8)
|
||||
===========
|
||||
|
||||
Name
|
||||
----
|
||||
pacstrap - install packages to the specified new root directory
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
pacstrap [options] root [packages...]
|
||||
|
||||
Description
|
||||
-----------
|
||||
pacstrap is designed to create a new system installation from scratch. The
|
||||
specified packages will be installed into a given directory after setting up
|
||||
some basic mountpoints. By default, the host system's pacman signing keys
|
||||
and mirrorlist will be used to seed the chroot.
|
||||
|
||||
If no packages are specified to be installed, the 'base' metapackage will be
|
||||
installed.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
*-C* <config>::
|
||||
Use an alternate config file for pacman.
|
||||
|
||||
*-c*::
|
||||
Use the package cache on the host, rather than the target.
|
||||
|
||||
*-D*::
|
||||
Skip pacman dependency checks.
|
||||
|
||||
*-G*::
|
||||
Avoid copying the host's pacman keyring to the target.
|
||||
|
||||
*-i*::
|
||||
Prompt for package confirmation when needed (run interactively).
|
||||
|
||||
*-K*::
|
||||
Initialize an empty pacman keyring in the target (implies '-G').
|
||||
|
||||
*-M*::
|
||||
Avoid copying the host's mirrorlist to the target.
|
||||
|
||||
*-N*::
|
||||
Run in unshare mode. This will use linkman:unshare[1] to create a new
|
||||
mount and user namespace, allowing regular users to create new system
|
||||
installations.
|
||||
|
||||
*-P*::
|
||||
Copy the host's pacman config to the target.
|
||||
|
||||
*-U*::
|
||||
Use pacman -U to install packages. Useful for obtaining fine-grained
|
||||
control over the installed packages.
|
||||
|
||||
*-h*::
|
||||
Output syntax and command line options.
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
linkman:pacman[8]
|
236
fstab-helpers
236
fstab-helpers
@ -1,236 +0,0 @@
|
||||
#!/hint/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
# generated from util-linux source: libmount/src/utils.c
|
||||
declare -A pseudofs_types=([anon_inodefs]=1
|
||||
[apparmorfs]=1
|
||||
[autofs]=1
|
||||
[bdev]=1
|
||||
[binder]=1
|
||||
[binfmt_misc]=1
|
||||
[bpf]=1
|
||||
[cgroup]=1
|
||||
[cgroup2]=1
|
||||
[configfs]=1
|
||||
[cpuset]=1
|
||||
[debugfs]=1
|
||||
[devfs]=1
|
||||
[devpts]=1
|
||||
[devtmpfs]=1
|
||||
[dlmfs]=1
|
||||
[dmabuf]=1
|
||||
[drm]=1
|
||||
[efivarfs]=1
|
||||
[fuse]=1
|
||||
[fuse.archivemount]=1
|
||||
[fuse.avfsd]=1
|
||||
[fuse.dumpfs]=1
|
||||
[fuse.encfs]=1
|
||||
[fuse.gvfs-fuse-daemon]=1
|
||||
[fuse.gvfsd-fuse]=1
|
||||
[fuse.lxcfs]=1
|
||||
[fuse.rofiles-fuse]=1
|
||||
[fuse.vmware-vmblock]=1
|
||||
[fuse.xwmfs]=1
|
||||
[fusectl]=1
|
||||
[hugetlbfs]=1
|
||||
[ipathfs]=1
|
||||
[mqueue]=1
|
||||
[nfsd]=1
|
||||
[none]=1
|
||||
[nsfs]=1
|
||||
[overlay]=1
|
||||
[pipefs]=1
|
||||
[proc]=1
|
||||
[pstore]=1
|
||||
[ramfs]=1
|
||||
[resctrl]=1
|
||||
[rootfs]=1
|
||||
[rpc_pipefs]=1
|
||||
[securityfs]=1
|
||||
[selinuxfs]=1
|
||||
[smackfs]=1
|
||||
[sockfs]=1
|
||||
[spufs]=1
|
||||
[sysfs]=1
|
||||
[tmpfs]=1
|
||||
[tracefs]=1
|
||||
[vboxsf]=1
|
||||
[virtiofs]=1)
|
||||
|
||||
# generated from: pkgfile -vbr '/fsck\..+' | awk -F. '{ print $NF }' | sort
|
||||
declare -A fsck_types=([btrfs]=0 # btrfs doesn't need a regular fsck utility
|
||||
[cramfs]=1
|
||||
[erofs]=1
|
||||
[exfat]=1
|
||||
[ext2]=1
|
||||
[ext3]=1
|
||||
[ext4]=1
|
||||
[f2fs]=1
|
||||
[fat]=1
|
||||
[jfs]=1
|
||||
[minix]=1
|
||||
[msdos]=1
|
||||
[reiserfs]=1
|
||||
[vfat]=1
|
||||
[xfs]=1)
|
||||
|
||||
# shellcheck disable=SC2059 # $1 and $2 can contain the printf modifiers
|
||||
out() { printf "$1 $2\n" "${@:3}"; }
|
||||
error() { out "==> ERROR:" "$@"; } >&2
|
||||
die() { error "$@"; exit 1; }
|
||||
|
||||
try_cast() (
|
||||
_=$(( $1#$2 ))
|
||||
) 2>/dev/null
|
||||
|
||||
valid_number_of_base() {
|
||||
local base=$1 len=${#2} i=
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
try_cast "$base" "${2:i:1}" || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mangle() {
|
||||
local i= chr= out=
|
||||
local {a..f}= {A..F}=
|
||||
|
||||
for (( i = 0; i < ${#1}; i++ )); do
|
||||
chr=${1:i:1}
|
||||
case $chr in
|
||||
[[:space:]\\])
|
||||
printf -v chr '%03o' "'$chr"
|
||||
out+=\\
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
done
|
||||
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
unmangle() {
|
||||
local i= chr= out= len=$(( ${#1} - 4 ))
|
||||
local {a..f}= {A..F}=
|
||||
|
||||
for (( i = 0; i < len; i++ )); do
|
||||
chr=${1:i:1}
|
||||
case $chr in
|
||||
\\)
|
||||
if valid_number_of_base 8 "${1:i+1:3}" ||
|
||||
valid_number_of_base 16 "${1:i+1:3}"; then
|
||||
printf -v chr '%b' "${1:i:4}"
|
||||
(( i += 3 ))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
out+=$chr
|
||||
done
|
||||
|
||||
printf '%s' "$out${1:i}"
|
||||
}
|
||||
|
||||
optstring_match_option() {
|
||||
local candidate pat patterns
|
||||
|
||||
IFS=, read -ra patterns <<<"$1"
|
||||
for pat in "${patterns[@]}"; do
|
||||
if [[ $pat = *=* ]]; then
|
||||
# "key=val" will only ever match "key=val"
|
||||
candidate=$2
|
||||
else
|
||||
# "key" will match "key", but also "key=anyval"
|
||||
candidate=${2%%=*}
|
||||
fi
|
||||
|
||||
[[ $pat = "$candidate" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
optstring_remove_option() {
|
||||
local o options_ remove=$2 IFS=,
|
||||
|
||||
read -ra options_ <<<"${!1}"
|
||||
|
||||
for o in "${!options_[@]}"; do
|
||||
optstring_match_option "$remove" "${options_[o]}" && unset 'options_[o]'
|
||||
done
|
||||
|
||||
declare -g "$1=${options_[*]}"
|
||||
}
|
||||
|
||||
optstring_normalize() {
|
||||
local o options_ norm IFS=,
|
||||
|
||||
read -ra options_ <<<"${!1}"
|
||||
|
||||
# remove empty fields
|
||||
for o in "${options_[@]}"; do
|
||||
[[ $o ]] && norm+=("$o")
|
||||
done
|
||||
|
||||
# avoid empty strings, reset to "defaults"
|
||||
declare -g "$1=${norm[*]:-defaults}"
|
||||
}
|
||||
|
||||
optstring_append_option() {
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=${!1},$2"
|
||||
fi
|
||||
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
optstring_prepend_option() {
|
||||
local options_=$1
|
||||
|
||||
if ! optstring_has_option "$1" "$2"; then
|
||||
declare -g "$1=$2,${!1}"
|
||||
fi
|
||||
|
||||
optstring_normalize "$1"
|
||||
}
|
||||
|
||||
optstring_get_option() {
|
||||
local _opts o
|
||||
|
||||
IFS=, read -ra _opts <<<"${!1}"
|
||||
for o in "${_opts[@]}"; do
|
||||
if optstring_match_option "$2" "$o"; then
|
||||
declare -g "$o"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
optstring_has_option() {
|
||||
local "${2%%=*}"
|
||||
|
||||
optstring_get_option "$1" "$2"
|
||||
}
|
||||
|
||||
dm_name_for_devnode() {
|
||||
read dm_name <"/sys/class/block/${1#/dev/}/dm/name"
|
||||
if [[ $dm_name ]]; then
|
||||
printf '/dev/mapper/%s' "$dm_name"
|
||||
else
|
||||
# don't leave the caller hanging, just print the original name
|
||||
# along with the failure.
|
||||
error 'Failed to resolve device mapper name for: %s' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
fstype_is_pseudofs() {
|
||||
(( pseudofs_types["$1"] ))
|
||||
}
|
||||
|
||||
fstype_has_fsck() {
|
||||
(( fsck_types["$1"] ))
|
||||
}
|
124
future-chroot.in
124
future-chroot.in
@ -1,124 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
unshare=0
|
||||
keepresolvconf=0
|
||||
|
||||
m4_include(common)
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: ${0##*/} chroot-dir [command] [arguments...]
|
||||
|
||||
-h Print this help message
|
||||
-N Run in unshare mode as a regular user
|
||||
-u <user>[:group] Specify non-root user and optional group to use
|
||||
-r Do not change the resolv.conf within the chroot
|
||||
|
||||
If 'command' is unspecified, ${0##*/} will launch /bin/bash.
|
||||
|
||||
Note that when using future-chroot, the target chroot directory *should* be a
|
||||
mountpoint. This ensures that tools such as pacman(8) or findmnt(8) have an
|
||||
accurate hierarchy of the mounted filesystems within the chroot.
|
||||
|
||||
If your chroot target is not a mountpoint, you can bind mount the directory on
|
||||
itself to make it a mountpoint, i.e. 'mount --bind /your/chroot /your/chroot'.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
resolve_link() {
|
||||
local target=$1
|
||||
local root=$2
|
||||
|
||||
# If a root was given, make sure it ends in a slash.
|
||||
[[ -n $root && $root != */ ]] && root=$root/
|
||||
|
||||
while [[ -L $target ]]; do
|
||||
target=$(readlink -m "$target")
|
||||
# If a root was given, make sure the target is under it.
|
||||
# Make sure to strip any leading slash from target first.
|
||||
[[ -n $root && $target != $root* ]] && target=$root${target#/}
|
||||
done
|
||||
|
||||
printf %s "$target"
|
||||
}
|
||||
|
||||
chroot_add_resolv_conf() {
|
||||
local chrootdir=$1
|
||||
local src
|
||||
local dest="$chrootdir/etc/resolv.conf"
|
||||
|
||||
src=$(resolve_link /etc/resolv.conf)
|
||||
|
||||
# If we don't have a source resolv.conf file, there's nothing useful we can do.
|
||||
[[ -e $src ]] || return 0
|
||||
|
||||
if [[ ! -e "$dest" && ! -h "$dest" ]]; then
|
||||
# There may be no resolv.conf in the chroot. In this case, we'll just exit.
|
||||
# The chroot environment must not be concerned with DNS resolution.
|
||||
return 0
|
||||
fi
|
||||
|
||||
chroot_add_mount "$src" "$dest" -c --bind
|
||||
}
|
||||
|
||||
future-chroot() {
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
|
||||
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
|
||||
|
||||
$setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
|
||||
if (( ! keepresolvconf )); then
|
||||
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
|
||||
fi
|
||||
|
||||
if ! mountpoint -q "$chrootdir"; then
|
||||
warning "$chrootdir is not a mountpoint. This may have undesirable side effects."
|
||||
fi
|
||||
|
||||
chroot_args=()
|
||||
[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
|
||||
|
||||
SHELL=/bin/bash $pid_unshare chroot "${chroot_args[@]}" -- "$chrootdir" "${args[@]}"
|
||||
}
|
||||
|
||||
while getopts ':hNu:r' flag; do
|
||||
case $flag in
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
N)
|
||||
unshare=1
|
||||
;;
|
||||
u)
|
||||
userspec=$OPTARG
|
||||
;;
|
||||
r)
|
||||
keepresolvconf=1
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
?)
|
||||
die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( $# )) || die 'No chroot directory specified'
|
||||
chrootdir=$1
|
||||
shift
|
||||
|
||||
args=("$@")
|
||||
if (( unshare )); then
|
||||
setup=unshare_setup
|
||||
$mount_unshare bash -c "$(declare_all); future-chroot"
|
||||
else
|
||||
setup=chroot_setup
|
||||
future-chroot
|
||||
fi
|
68
genfstab.in
68
genfstab.in
@ -1,9 +1,8 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
m4_include(fstab-helpers)
|
||||
m4_include(common)
|
||||
|
||||
write_source() {
|
||||
local src=$1 spec= label= uuid= comment=()
|
||||
@ -59,22 +58,11 @@ optstring_apply_quirks() {
|
||||
fi
|
||||
|
||||
case $fstype in
|
||||
btrfs)
|
||||
# Having only one of subvol= and subvolid= is enough for mounting a btrfs subvolume
|
||||
# And having subvolid= set prevents things like 'snapper rollback' to work, as it
|
||||
# updates the subvolume in-place, leaving subvol= unchanged with a different subvolid.
|
||||
if optstring_has_option "$varname" subvol; then
|
||||
optstring_remove_option "$varname" subvolid
|
||||
fi
|
||||
;;
|
||||
f2fs)
|
||||
# These are build-time or runtime-unchangeable options for f2fs.
|
||||
# The former means that kernels supporting the options will only
|
||||
# provide the negative versions of these (e.g. noacl), and vice versa
|
||||
# These are Kconfig options for f2fs. Kernels supporting the options will
|
||||
# only provide the negative versions of these (e.g. noacl), and vice versa
|
||||
# for kernels without support.
|
||||
# The latter means that the options can only be specified/changed
|
||||
# during the initial mount but not remount.
|
||||
optstring_remove_option "$varname" noacl,acl,nouser_xattr,user_xattr,atgc
|
||||
optstring_remove_option "$varname" noacl,acl,nouser_xattr,user_xattr
|
||||
;;
|
||||
vfat)
|
||||
# Before Linux v3.8, "cp" is prepended to the value of the codepage.
|
||||
@ -91,12 +79,11 @@ usage() {
|
||||
usage: ${0##*/} [options] root
|
||||
|
||||
Options:
|
||||
-f <filter> Restrict output to mountpoints matching the prefix FILTER
|
||||
-f FILTER Restrict output to mountpoints matching the prefix FILTER
|
||||
-L Use labels for source identifiers (shortcut for -t LABEL)
|
||||
-p Exclude pseudofs mounts (default behavior)
|
||||
-P Include pseudofs mounts
|
||||
-t <tag> Use TAG for source identifiers (TAG should be one of: LABEL,
|
||||
UUID, PARTLABEL, PARTUUID)
|
||||
-t TAG Use TAG for source identifiers
|
||||
-U Use UUIDs for source identifiers (shortcut for -t UUID)
|
||||
|
||||
-h Print this help message
|
||||
@ -150,7 +137,8 @@ if ! mountpoint -q "$root"; then
|
||||
fi
|
||||
|
||||
# handle block devices
|
||||
while read -r src target fstype opts fsroot; do
|
||||
findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root" |
|
||||
while read -r src target fstype opts fsroot; do
|
||||
if (( !pseudofs )) && fstype_is_pseudofs "$fstype"; then
|
||||
continue
|
||||
fi
|
||||
@ -174,20 +162,23 @@ while read -r src target fstype opts fsroot; do
|
||||
pass=0
|
||||
fi
|
||||
|
||||
if [[ $fsroot != / && $fstype != btrfs ]]; then
|
||||
# it's a bind mount
|
||||
src=$(findmnt -funcevo TARGET "$src")$fsroot
|
||||
src="/${src#$root/}"
|
||||
if [[ $src -ef $target ]]; then
|
||||
# hrmm, this is weird. we're probably looking at a file or directory
|
||||
# that was bound into a chroot from the host machine. Ignore it,
|
||||
# because this won't actually be a valid mount. Worst case, the user
|
||||
# just re-adds it.
|
||||
continue
|
||||
if [[ $fsroot != / ]]; then
|
||||
if [[ $fstype = btrfs ]]; then
|
||||
opts+=,subvol=${fsroot#/}
|
||||
else
|
||||
# it's a bind mount
|
||||
src=$(findmnt -funcevo TARGET "$src")$fsroot
|
||||
if [[ $src -ef $target ]]; then
|
||||
# hrmm, this is weird. we're probably looking at a file or directory
|
||||
# that was bound into a chroot from the host machine. Ignore it,
|
||||
# because this won't actually be a valid mount. Worst case, the user
|
||||
# just re-adds it.
|
||||
continue
|
||||
fi
|
||||
fstype=none
|
||||
opts+=,bind
|
||||
pass=0
|
||||
fi
|
||||
fstype=none
|
||||
opts+=,bind
|
||||
pass=0
|
||||
fi
|
||||
|
||||
# filesystem quirks
|
||||
@ -211,7 +202,7 @@ while read -r src target fstype opts fsroot; do
|
||||
printf '\t%-10s' "/$(mangle "${target#/}")" "$fstype" "$opts"
|
||||
printf '\t%s %s' "$dump" "$pass"
|
||||
printf '\n\n'
|
||||
done < <(findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root")
|
||||
done
|
||||
|
||||
# handle swaps devices
|
||||
{
|
||||
@ -220,18 +211,15 @@ done < <(findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root")
|
||||
|
||||
while read -r device type _ _ prio; do
|
||||
options=defaults
|
||||
if (( prio >= 0 )); then
|
||||
if [[ $prio != -1 ]]; then
|
||||
options+=,pri=$prio
|
||||
fi
|
||||
|
||||
# skip files marked deleted by the kernel
|
||||
[[ $device = *'\040(deleted)' ]] && continue
|
||||
|
||||
# skip devices not part of the prefix
|
||||
[[ $device = "$prefixfilter"* ]] || continue
|
||||
|
||||
if [[ $type = file ]]; then
|
||||
printf '%-20s' "${device#${root%/}}"
|
||||
printf '%-20s' "$device"
|
||||
elif [[ $device = /dev/dm-+([0-9]) ]]; then
|
||||
# device mapper doesn't allow characters we need to worry
|
||||
# about being mangled, and it does the escaping of dashes
|
||||
@ -244,3 +232,5 @@ done < <(findmnt -Recvruno SOURCE,TARGET,FSTYPE,OPTIONS,FSROOT "$root")
|
||||
printf '\t%-10s\t%-10s\t%-10s\t0 0\n\n' 'none' 'swap' "$options"
|
||||
done
|
||||
} </proc/swaps
|
||||
|
||||
# vim: et ts=2 sw=2 ft=sh:
|
||||
|
136
pacstrap.in
136
pacstrap.in
@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#
|
||||
# Assumptions:
|
||||
@ -11,33 +10,22 @@
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
m4_include(common)
|
||||
|
||||
hostcache=0
|
||||
copykeyring=1
|
||||
initkeyring=0
|
||||
copymirrorlist=1
|
||||
pacman_args=()
|
||||
pacmode=-Sy
|
||||
unshare=0
|
||||
copyconf=0
|
||||
pacman_config=/etc/pacman.conf
|
||||
|
||||
m4_include(common)
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: ${0##*/} [options] root [packages...]
|
||||
|
||||
Options:
|
||||
-C <config> Use an alternate config file for pacman
|
||||
-C config Use an alternate config file for pacman
|
||||
-c Use the package cache on the host, rather than the target
|
||||
-D Skip pacman dependency checks
|
||||
-G Avoid copying the host's pacman keyring to the target
|
||||
-i Prompt for package confirmation when needed (run interactively)
|
||||
-K Initialize an empty pacman keyring in the target (implies '-G')
|
||||
-i Avoid auto-confirmation of package selections
|
||||
-M Avoid copying the host's mirrorlist to the target
|
||||
-N Run in unshare mode as a regular user
|
||||
-P Copy the host's pacman config to the target
|
||||
-U Use pacman -U to install packages
|
||||
|
||||
-h Print this help message
|
||||
|
||||
@ -47,57 +35,20 @@ are given, pacstrap defaults to the "base" group.
|
||||
EOF
|
||||
}
|
||||
|
||||
pacstrap() {
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
|
||||
# create obligatory directories
|
||||
msg 'Creating install root at %s' "$newroot"
|
||||
# shellcheck disable=SC2174 # permissions are perfectly fine here
|
||||
mkdir -m 0755 -p "$newroot"/var/{cache/pacman/pkg,lib/pacman,log} "$newroot"/{dev,run,etc/pacman.d}
|
||||
# shellcheck disable=SC2174 # permissions are perfectly fine here
|
||||
mkdir -m 1777 -p "$newroot"/tmp
|
||||
# shellcheck disable=SC2174 # permissions are perfectly fine here
|
||||
mkdir -m 0555 -p "$newroot"/{sys,proc}
|
||||
|
||||
# mount API filesystems
|
||||
$setup "$newroot" || die "failed to setup chroot %s" "$newroot"
|
||||
|
||||
if [[ ! -d $newroot/etc/pacman.d/gnupg ]]; then
|
||||
if (( initkeyring )); then
|
||||
pacman-key --gpgdir "$newroot"/etc/pacman.d/gnupg --init
|
||||
elif (( copykeyring )) && [[ -d /etc/pacman.d/gnupg ]]; then
|
||||
# if there's a keyring on the host, copy it into the new root
|
||||
cp -a --no-preserve=ownership /etc/pacman.d/gnupg "$newroot/etc/pacman.d/"
|
||||
fi
|
||||
fi
|
||||
|
||||
msg 'Installing packages to %s' "$newroot"
|
||||
if ! $pid_unshare pacman -r "$newroot" "${pacman_args[@]}"; then
|
||||
die 'Failed to install packages to new root'
|
||||
fi
|
||||
|
||||
if (( copymirrorlist )); then
|
||||
# install the host's mirrorlist onto the new root
|
||||
cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/"
|
||||
fi
|
||||
|
||||
if (( copyconf )); then
|
||||
cp -a "$pacman_config" "$newroot/etc/pacman.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -z $1 || $1 = @(-h|--help) ]]; then
|
||||
usage
|
||||
exit $(( $# ? 0 : 1 ))
|
||||
fi
|
||||
|
||||
while getopts ':C:cDGiKMNPU' flag; do
|
||||
(( EUID == 0 )) || die 'This script must be run with root privileges'
|
||||
|
||||
while getopts ':C:cdGiM' flag; do
|
||||
case $flag in
|
||||
C)
|
||||
pacman_config=$OPTARG
|
||||
;;
|
||||
D)
|
||||
pacman_args+=(-dd)
|
||||
d)
|
||||
# retired flag. does nothing.
|
||||
;;
|
||||
c)
|
||||
hostcache=1
|
||||
@ -108,21 +59,9 @@ while getopts ':C:cDGiKMNPU' flag; do
|
||||
G)
|
||||
copykeyring=0
|
||||
;;
|
||||
K)
|
||||
initkeyring=1
|
||||
;;
|
||||
M)
|
||||
copymirrorlist=0
|
||||
;;
|
||||
N)
|
||||
unshare=1
|
||||
;;
|
||||
P)
|
||||
copyconf=1
|
||||
;;
|
||||
U)
|
||||
pacmode=-U
|
||||
;;
|
||||
:)
|
||||
die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
|
||||
;;
|
||||
@ -134,34 +73,45 @@ done
|
||||
shift $(( OPTIND - 1 ))
|
||||
|
||||
(( $# )) || die "No root directory specified"
|
||||
newroot=$1
|
||||
shift
|
||||
|
||||
[[ -d $newroot ]] || die "%s is not a directory" "$newroot"
|
||||
|
||||
tmpfile="$(mktemp -t pacman.conf.XXXX)"
|
||||
cp "$pacman_config" "$tmpfile"
|
||||
sed -i 's/^DownloadUser/#&/' "$tmpfile"
|
||||
pacman_config="$tmpfile"
|
||||
|
||||
pacman_args+=("$pacmode" "${@:-base}" --config="$pacman_config" --disable-sandbox)
|
||||
|
||||
if (( ! hostcache )); then
|
||||
pacman_args+=(--cachedir="$newroot/var/cache/pacman/pkg")
|
||||
fi
|
||||
newroot=$1; shift
|
||||
pacman_args=("${@:-base}")
|
||||
|
||||
if (( ! interactive )); then
|
||||
pacman_args+=(--noconfirm)
|
||||
fi
|
||||
|
||||
if (( unshare )); then
|
||||
setup=unshare_setup
|
||||
$mount_unshare bash -c "$(declare_all); pacstrap"
|
||||
[[ -d $newroot ]] || die "%s is not a directory" "$newroot"
|
||||
|
||||
# create obligatory directories
|
||||
msg 'Creating install root at %s' "$newroot"
|
||||
mkdir -m 0755 -p "$newroot"/var/{cache/pacman/pkg,lib/pacman,log} "$newroot"/{dev,run,etc/pacman.d}
|
||||
mkdir -m 1777 -p "$newroot"/tmp
|
||||
mkdir -m 0555 -p "$newroot"/{sys,proc}
|
||||
|
||||
# mount API filesystems
|
||||
chroot_setup "$newroot" || die "failed to setup chroot %s" "$newroot"
|
||||
|
||||
if [[ $pacman_config ]]; then
|
||||
pacman-conf --config "$pacman_config" "$newroot/tmp/pacman.conf"
|
||||
else
|
||||
setup=chroot_setup
|
||||
pacstrap
|
||||
pacman-conf > "$newroot/tmp/pacman.conf"
|
||||
fi
|
||||
|
||||
# TODO: There is a trap check on exit. Need to rework the trap handling with
|
||||
# hook-ins/callbacks to remove aux files
|
||||
rm "$tmpfile"
|
||||
if (( copykeyring )); then
|
||||
# if there's a keyring on the host, copy it into the new root, unless it exists already
|
||||
if [[ -d /etc/pacman.d/gnupg && ! -d $newroot/etc/pacman.d/gnupg ]]; then
|
||||
cp -a /etc/pacman.d/gnupg "$newroot/etc/pacman.d/"
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( copymirrorlist )); then
|
||||
# install the host's mirrorlist onto the new root
|
||||
cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/"
|
||||
fi
|
||||
|
||||
msg 'Installing packages to %s' "$newroot"
|
||||
if ! pacman --sysroot "$newroot" --config /tmp/pacman.conf -Sy "${pacman_args[@]}"; then
|
||||
die 'Failed to install packages to new root'
|
||||
fi
|
||||
|
||||
# vim: et ts=2 sw=2 ft=sh:
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./fstab-helpers
|
||||
. "${1:-./common}"
|
||||
. ./test/common
|
||||
|
||||
ASSERT_streq ' deleted' "$(unmangle "$(mangle ' deleted')")"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./fstab-helpers
|
||||
. "${1:-./common}"
|
||||
. ./test/common
|
||||
|
||||
optstring=rw,relatime,fd=29,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
|
||||
@ -26,10 +26,8 @@ EXPECT_success optstring_has_option optstring maxproto
|
||||
|
||||
EXPECT_failure optstring_get_option optstring proto
|
||||
EXPECT_success optstring_get_option optstring maxproto
|
||||
# shellcheck disable=SC2154 # set via the optstring helper above
|
||||
ASSERT_streq "$maxproto" "5"
|
||||
EXPECT_success optstring_get_option optstring timeout
|
||||
# shellcheck disable=SC2154 # set via the optstring helper above
|
||||
ASSERT_streq "$timeout" "300"
|
||||
|
||||
optstring_remove_option optstring pgrp
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./fstab-helpers
|
||||
. "${1:-./common}"
|
||||
. ./test/common
|
||||
|
||||
EXPECT_success valid_number_of_base 16 feedfacebeef
|
||||
|
@ -1,20 +1,13 @@
|
||||
#compdef pacstrap
|
||||
#compdef pacstrap genfstab arch-chroot
|
||||
|
||||
_pacstrap_args=(
|
||||
'-h[display help]'
|
||||
)
|
||||
|
||||
_pacstrap_args_nonh=(
|
||||
'-C[Use an alternate config file for pacman]:config file:_files -/'
|
||||
'-c[Use the package cache on the host, rather than the target]'
|
||||
'-D[Skip pacman dependency checks]'
|
||||
'-G[Avoid copying the host pacman keyring to the target]'
|
||||
'-i[Prompt for package confirmation when needed (run interactively)]'
|
||||
'-K[Initialize an empty pacman keyring in the target (implies -G)]'
|
||||
'-M[Avoid copying the host mirrorlist to the target]'
|
||||
'-N[Run in unshare mode as a regular user]'
|
||||
'-P[Copy the host pacman config to the target]'
|
||||
'-U[Use pacman -U to install packages]'
|
||||
'(-h --help)-c[Use the package cache on the host, rather than the target]'
|
||||
'(-h --help)-d[Allow installation to a non-mountpoint directory]'
|
||||
'(--help -h)-i[Avoid auto-confirmation of package selections]'
|
||||
)
|
||||
|
||||
|
||||
@ -59,6 +52,17 @@ _pacstrap_none(){
|
||||
"$_longopts[@]" \
|
||||
}
|
||||
|
||||
_genfstab_args=(
|
||||
'-h[display help]'
|
||||
)
|
||||
_genfstab_args_nonh=(
|
||||
'(--help -h)-p[Avoid printing pseudofs mounts]'
|
||||
'(-U --help -h)-L[Use labels for source identifiers]'
|
||||
'(-L --help -h)-U[Use UUIDs for source identifiers]'
|
||||
)
|
||||
|
||||
_arch_chroot_args=( '-h[display help]' )
|
||||
|
||||
_longopts=( '--help[display help]' )
|
||||
|
||||
_pacstrap(){
|
||||
@ -91,10 +95,69 @@ _pacstrap(){
|
||||
fi
|
||||
}
|
||||
|
||||
_genfstab(){
|
||||
if [[ -z ${(M)words:#--help} && -z ${(M)words:#-*h} ]]; then
|
||||
case $words[CURRENT] in
|
||||
-p*|-L*|-U*)
|
||||
_arguments -s : \
|
||||
"$_genfstab_args_nonh[@]"
|
||||
;;
|
||||
-*)
|
||||
_arguments -s : \
|
||||
"$_genfstab_args[@]" \
|
||||
"$_genfstab_args_nonh[@]" \
|
||||
"$_longopts[@]"
|
||||
;;
|
||||
--*)
|
||||
_arguments -s : \
|
||||
"$_longopts[@]"
|
||||
;;
|
||||
*)
|
||||
_arguments \
|
||||
"$_genfstab_args[@]" \
|
||||
"$_genfstab_args_nonh[@]" \
|
||||
"$_longopts[@]" \
|
||||
":*:_path_files -/"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_arch_chroot(){
|
||||
if [[ -z ${(M)words:#--help} && -z ${(M)words:#-*h} ]]; then
|
||||
case $words[CURRENT] in
|
||||
-*)
|
||||
_arguments -s : \
|
||||
"$_arch_chroot_args[@]" \
|
||||
"$_longopts[@]" \
|
||||
;;
|
||||
--*)
|
||||
_arguments -s : \
|
||||
"$_longopts[@]"
|
||||
;;
|
||||
*)
|
||||
_arguments \
|
||||
':*:_path_files -/'
|
||||
;;
|
||||
esac
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_install_scripts(){
|
||||
case "$service" in
|
||||
pacstrap)
|
||||
_pacstrap "$@";;
|
||||
_pacstrap "$@"
|
||||
;;
|
||||
genfstab)
|
||||
_genfstab "$@";;
|
||||
arch-chroot)
|
||||
_arch_chroot "$@";;
|
||||
*)
|
||||
_message "Error";;
|
||||
esac
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user