Compare commits

..

3 Commits

Author SHA1 Message Date
Eli Schwartz
db0b093076
pacstrap: use --sysroot instead of --root
Does not seem to work, there's no internet connection in the chroot
2018-06-18 03:23:01 -04:00
Eli Schwartz
8f7fc3f5d7
Generate or copy a pacman.conf into the newroot's /tmp directory
This makes pacstrap more compatible with migrating from --root to
--sysroot, since the pacman.conf will be required to come from the
newroot itself.
2018-06-18 03:21:48 -04:00
Eli Schwartz
e418a17468
Use bind mounts to reuse the hostcache
This avoids depending on pacman --root using the host config, making it
easier to move to --sysroot instead.
2018-05-29 12:03:38 -04:00
24 changed files with 452 additions and 1012 deletions

View File

@ -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
View File

@ -2,5 +2,4 @@
arch-chroot
genfstab
pacstrap
doc/*.8
arch-install-scripts-*.tar.gz*

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View File

@ -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"] ))
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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=&#42;
plus=&#43;
caret=&#94;
startsb=&#91;
endsb=&#93;
backslash=&#92;
tilde=&#126;
apostrophe=&#39;
backtick=&#96;
litdd=&#45;&#45;
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[]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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"] ))
}

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -1,6 +1,6 @@
#!/bin/bash
. ./fstab-helpers
. "${1:-./common}"
. ./test/common
ASSERT_streq ' deleted' "$(unmangle "$(mangle ' deleted')")"

View File

@ -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

View File

@ -1,6 +1,6 @@
#!/bin/bash
. ./fstab-helpers
. "${1:-./common}"
. ./test/common
EXPECT_success valid_number_of_base 16 feedfacebeef

View File

@ -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
}