reinstall/trans.sh

5355 lines
166 KiB
Bash
Raw Normal View History

2023-05-13 00:14:46 +08:00
#!/bin/ash
# shellcheck shell=dash
2024-10-13 22:58:12 +08:00
# shellcheck disable=SC2086,SC3047,SC3036,SC3010,SC3001,SC3060
2023-05-13 00:14:46 +08:00
# alpine 默认使用 busybox ash
2024-08-20 00:27:51 +08:00
# 出错后停止运行,将进入到登录界面,防止失联
2023-06-18 21:27:22 +08:00
set -eE
2024-05-22 21:56:11 +08:00
2024-09-06 23:00:28 +08:00
# 用于判断 reinstall.sh 和 trans.sh 是否兼容
# shellcheck disable=SC2034
2024-10-13 22:58:12 +08:00
SCRIPT_VERSION=4BACD833-A585-23BA-6CBB-9AA4E08E0002
2024-05-22 21:56:11 +08:00
TRUE=0
FALSE=1
2024-08-19 00:33:59 +08:00
EFI_UUID=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
2024-08-19 00:33:59 +08:00
error() {
color='\e[31m'
plain='\e[0m'
2024-10-18 23:34:54 +08:00
echo -e "${color}***** ERROR *****${plain}" >&2
2024-08-19 00:33:59 +08:00
echo -e "${color}Error: $*${plain}" >&2
}
2023-09-16 20:00:05 +08:00
2024-09-06 23:00:36 +08:00
info() {
color='\e[32m'
plain='\e[0m'
echo -e "${color}***** $(echo "$*" | to_upper) *****${plain}" >&2
}
2024-08-19 00:33:59 +08:00
error_and_exit() {
error "$@"
exit 1
}
2023-09-16 20:00:05 +08:00
trap_err() {
line_no=$1
ret_no=$2
error "Line $line_no return $ret_no"
2024-08-19 00:33:59 +08:00
if [ -f "/trans.sh" ]; then
sed -n "$line_no"p /trans.sh
2023-06-18 21:27:22 +08:00
fi
}
2024-08-19 00:33:59 +08:00
is_run_from_locald() {
2024-08-20 00:27:51 +08:00
[[ "$0" = "/etc/local.d/*" ]]
2023-07-25 00:21:08 +08:00
}
2023-05-25 20:15:12 +08:00
add_community_repo() {
2023-08-25 13:04:06 +08:00
# 先检查原来的repo是不是egde
if grep -q '^http.*/edge/main$' /etc/apk/repositories; then
2023-08-25 13:04:06 +08:00
alpine_ver=edge
else
alpine_ver=v$(cut -d. -f1,2 </etc/alpine-release)
fi
if ! grep -q "^http.*/$alpine_ver/community$" /etc/apk/repositories; then
2024-03-31 00:32:05 +08:00
alpine_mirror=$(grep '^http.*/main$' /etc/apk/repositories | sed 's,/[^/]*/main$,,' | head -1)
echo $alpine_mirror/$alpine_ver/community >>/etc/apk/repositories
2023-07-05 21:57:27 +08:00
fi
2023-05-25 20:15:12 +08:00
}
2024-03-05 23:01:24 +08:00
# 有时网络问题下载失败,导致脚本中断
# 因此需要重试
apk() {
2024-09-06 23:00:35 +08:00
retry 5 command apk "$@" >&2
2024-03-05 23:01:24 +08:00
}
2024-09-06 23:00:35 +08:00
# 在没有设置 set +o pipefail 的情况下,限制下载大小:
# retry 5 command wget | head -c 1048576 会触发 retry下载 5 次
# command wget "$@" --tries=5 | head -c 1048576 不会触发 wget 自带的 retry只下载 1 次
wget() {
2024-03-18 23:56:10 +08:00
echo "$@" | grep -o 'http[^ ]*' >&2
2024-09-06 23:00:35 +08:00
if command wget 2>&1 | grep -q BusyBox; then
# busybox wget 没有重试功能
2024-10-12 23:07:01 +08:00
# 好像默认永不超时
retry 5 command wget "$@" -T 10
2024-09-06 23:00:35 +08:00
else
# 原版 wget 自带重试功能
command wget --tries=5 --progress=bar:force "$@"
fi
}
is_have_cmd() {
command -v "$1" >/dev/null
}
is_have_cmd_on_disk() {
os_dir=$1
cmd=$2
for bin_dir in /bin /sbin /usr/bin /usr/sbin; do
if [ -f "$os_dir$bin_dir/$cmd" ]; then
return
fi
done
return 1
}
retry() {
max_try=$1
shift
for i in $(seq $max_try); do
if "$@"; then
return
else
ret=$?
if [ $i -ge $max_try ]; then
return $ret
fi
sleep 1
fi
done
}
2023-06-18 21:27:22 +08:00
download() {
2023-07-03 23:11:10 +08:00
url=$1
path=$2
2023-06-18 21:27:22 +08:00
# 有ipv4地址无ipv4网关的情况下aria2可能会用ipv4下载而不是ipv6
2023-07-03 23:11:10 +08:00
# axel 在 lightsail 上会占用大量cpu
# aria2 下载 fedora 官方镜像链接会将meta4文件下载下来而且占用了指定文件名造成重命名失效。而且无法指定目录
# https://download.opensuse.org/distribution/leap/15.5/appliances/openSUSE-Leap-15.5-Minimal-VM.x86_64-kvm-and-xen.qcow2
# https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-o
# 构造 aria2 参数
# 没有指定文件名的情况
if [ -z "$path" ]; then
save=""
else
# 文件名是绝对路径
if [[ "$path" = '/*' ]]; then
save="-d / -o $path"
else
# 文件名是相对路径
save="-o $path"
fi
fi
if ! is_have_cmd aria2c; then
2024-03-17 00:08:48 +08:00
apk add aria2
fi
# stdbuf 在 coreutils 包里面
if ! is_have_cmd stdbuf; then
apk add coreutils
2023-06-18 21:27:22 +08:00
fi
2024-05-22 22:32:43 +08:00
# 阿里云源限速,而且检测 user-agent 禁止 axel/aria2 下载
2023-12-02 23:14:43 +08:00
# aria2 默认 --max-tries 5
2024-01-27 23:17:50 +08:00
# 默认 --max-tries=5但以下情况服务器出错aria2不会重试而是直接返回错误
# 因此添加 for 循环
# [ERROR] CUID#7 - Download aborted. URI=https://aka.ms/manawindowsdrivers
# Exception: [AbstractCommand.cc:351] errorCode=1 URI=https://aka.ms/manawindowsdrivers
# -> [SocketCore.cc:1019] errorCode=1 SSL/TLS handshake failure: `not signed by known authorities or invalid'
# 用 if 的话,报错不会中断脚本
# if aria2c xxx; then
# return
# fi
2024-04-06 21:35:48 +08:00
# --user-agent=Wget/1.21.1 \
2024-01-27 23:17:50 +08:00
echo "$url"
retry 5 stdbuf -oL -eL aria2c -x4 \
--allow-overwrite=true \
--summary-interval=0 \
--max-tries 1 \
$save "$url"
2023-06-18 21:27:22 +08:00
}
update_part() {
2023-12-13 21:56:37 +08:00
sleep 1
sync
# partprobe
if is_have_cmd partprobe; then
2023-12-13 21:56:37 +08:00
partprobe /dev/$xda 2>/dev/null
fi
2023-12-13 21:56:37 +08:00
# partx
# https://access.redhat.com/solutions/199573
if is_have_cmd partx; then
partx -u /dev/$xda
fi
2023-12-13 21:56:37 +08:00
# mdev
# mdev 不会删除 /dev/disk/ 的旧分区,因此手动删除
# 如果 rm -rf 的时候刚好 mdev 在创建链接rm -rf 会报错 Directory not empty
# 因此要先停止 mdev 服务
# 还要删除 /dev/$xda*?
ensure_service_stopped mdev
rm -rf /dev/disk/*
# 没挂载 modloop 时会提示
# modprobe: can't change directory to '/lib/modules': No such file or directory
# 因此强制不显示上面的提示
mdev -sf 2>/dev/null
ensure_service_started mdev 2>/dev/null
sleep 1
2023-07-18 00:01:07 +08:00
}
2023-06-18 21:27:22 +08:00
2023-07-06 22:20:09 +08:00
is_efi() {
2024-07-09 23:37:51 +08:00
if [ -n "$force" ]; then
[ "$force" = efi ]
else
[ -d /sys/firmware/efi/ ]
fi
2023-07-06 22:20:09 +08:00
}
2023-07-18 00:22:51 +08:00
is_use_cloud_image() {
[ -n "$cloud_image" ] && [ "$cloud_image" = 1 ]
}
2024-10-12 23:07:01 +08:00
is_allow_ping() {
[ -n "$allow_ping" ] && [ "$allow_ping" = 1 ]
}
2023-07-08 16:02:06 +08:00
setup_nginx() {
apk add nginx
2023-10-25 14:19:25 +08:00
# shellcheck disable=SC2154
wget $confhome/logviewer.html -O /logviewer.html
wget $confhome/logviewer-nginx.conf -O /etc/nginx/http.d/default.conf
2024-10-12 23:07:01 +08:00
if [ -z "$web_port" ]; then
web_port=80
fi
sed -i "s/@WEB_PORT@/$web_port/gi" /etc/nginx/http.d/default.conf
# rc-service -q nginx start
2023-07-18 00:01:07 +08:00
if pgrep nginx >/dev/null; then
nginx -s reload
else
nginx
fi
2023-07-08 16:02:06 +08:00
}
2024-10-18 23:34:54 +08:00
setup_websocketd() {
apk add websocketd
wget $confhome/logviewer.html -O /tmp/index.html
apk add coreutils
2024-10-19 17:27:14 +08:00
if [ -z "$web_port" ]; then
web_port=80
fi
2024-10-21 00:30:02 +08:00
pkill websocketd || true
2024-10-18 23:34:54 +08:00
# websocketd 遇到 \n 才推送,因此要转换 \r 为 \n
2024-10-19 17:27:14 +08:00
websocketd --port "$web_port" --loglevel=fatal --staticdir=/tmp \
2024-10-18 23:34:54 +08:00
stdbuf -oL -eL sh -c "tail -fn+0 /reinstall.log | tr '\r' '\n'" &
}
2023-12-02 23:30:39 +08:00
get_approximate_ram_size() {
# lsmem 需要 util-linux
2024-03-28 00:16:05 +08:00
if false && is_have_cmd lsmem; then
2023-12-02 23:30:39 +08:00
ram_size=$(lsmem -b 2>/dev/null | grep 'Total online memory:' | awk '{ print $NF/1024/1024 }')
fi
if [ -z $ram_size ]; then
ram_size=$(free -m | awk '{print $2}' | sed -n '2p')
fi
echo "$ram_size"
}
setup_web_if_enough_ram() {
2023-12-02 23:30:39 +08:00
total_ram=$(get_approximate_ram_size)
# 512内存才安装
2023-07-25 00:21:08 +08:00
if [ $total_ram -gt 400 ]; then
# lighttpd 虽然运行占用内存少,但安装占用空间大
# setup_lighttpd
2024-10-18 23:34:54 +08:00
# setup_nginx
setup_websocketd
2023-07-25 00:21:08 +08:00
fi
}
2023-07-08 16:02:06 +08:00
setup_lighttpd() {
apk add lighttpd
ln -sf /reinstall.html /var/www/localhost/htdocs/index.html
rc-service -q lighttpd start
2023-07-08 16:02:06 +08:00
}
2023-08-08 23:21:28 +08:00
get_ttys() {
prefix=$1
2023-09-16 20:00:05 +08:00
# shellcheck disable=SC2154
2023-09-03 19:35:01 +08:00
wget $confhome/ttys.sh -O- | sh -s $prefix
2023-08-08 23:21:28 +08:00
}
2024-02-01 00:59:34 +08:00
find_xda() {
2024-10-18 23:34:57 +08:00
# 出错后再运行脚本,硬盘可能已经格式化,之前记录的分区表 id 无效
# 因此找到 xda 后要保存 xda 到 /config/xda
# 先读取之前保存的
if xda=$(get_config xda 2>/dev/null) && [ -n "$xda" ]; then
return
fi
2024-02-01 00:59:34 +08:00
# 防止 $main_disk 为空
if [ -z "$main_disk" ]; then
error_and_exit "cmdline main_disk is empty."
fi
2024-04-16 23:34:27 +08:00
# busybox fdisk/lsblk/blkid 不显示 mbr 分区表 id
2024-03-01 22:01:22 +08:00
# 可用以下工具:
2024-02-01 00:59:34 +08:00
# fdisk 在 util-linux-misc 里面,占用大
# sfdisk 占用小
2024-03-01 22:01:22 +08:00
# lsblk
2024-04-16 23:34:27 +08:00
# blkid
2024-02-02 00:49:24 +08:00
2024-03-01 22:01:22 +08:00
tool=sfdisk
2024-02-01 00:59:34 +08:00
2024-03-01 22:01:22 +08:00
is_have_cmd $tool && need_install_tool=false || need_install_tool=true
if $need_install_tool; then
apk add $tool
fi
if [ "$tool" = sfdisk ]; then
# sfdisk
for disk in $(get_all_disks); do
if sfdisk --disk-id "/dev/$disk" | sed 's/0x//' | grep -ix "$main_disk"; then
xda=$disk
break
fi
done
else
# lsblk
xda=$(lsblk --nodeps -rno NAME,PTUUID | grep -iw "$main_disk" | awk '{print $1}')
fi
2024-02-01 00:59:34 +08:00
2024-10-18 23:34:57 +08:00
if [ -n "$xda" ]; then
set_config xda "$xda"
else
2024-02-01 00:59:34 +08:00
error_and_exit "Could not find xda: $main_disk"
fi
2024-03-01 22:01:22 +08:00
if $need_install_tool; then
apk del $tool
fi
2024-02-01 00:59:34 +08:00
}
get_all_disks() {
2024-05-29 21:30:24 +08:00
# shellcheck disable=SC2010
ls /sys/block/ | grep -Ev '^(loop|sr|nbd)'
}
extract_env_from_cmdline() {
# 提取 finalos/extra 到变量
for prefix in finalos extra; do
while read -r line; do
if [ -n "$line" ]; then
key=$(echo $line | cut -d= -f1)
value=$(echo $line | cut -d= -f2-)
eval "$key='$value'"
fi
done < <(xargs -n1 </proc/cmdline | grep "^${prefix}_" | sed "s/^${prefix}_//")
done
}
ensure_service_started() {
service=$1
if ! rc-service -q $service status; then
if ! retry 5 rc-service -q $service start; then
error_and_exit "Failed to start $service."
fi
fi
}
ensure_service_stopped() {
service=$1
if rc-service -q $service status; then
if ! retry 5 rc-service -q $service stop; then
error_and_exit "Failed to stop $service."
fi
fi
}
mod_motd() {
# 安装后 alpine 后要恢复默认
2023-11-28 23:59:17 +08:00
# 自动安装失败后,可能手动安装 alpine因此无需判断 $distro
2023-12-02 23:30:39 +08:00
file=/etc/motd
if ! [ -e $file.orig ]; then
cp $file $file.orig
# shellcheck disable=SC2016
2023-12-02 23:30:39 +08:00
echo "mv "\$mnt$file.orig" "\$mnt$file"" |
insert_into_file "$(which setup-disk)" before 'cleanup_chroot_mounts "\$mnt"'
2023-12-02 23:30:39 +08:00
cat <<EOF >$file
Reinstalling...
To view logs run:
2023-10-25 14:19:25 +08:00
tail -fn+1 /reinstall.log
EOF
2023-12-02 23:30:39 +08:00
fi
}
umount_all() {
2024-08-19 00:42:12 +08:00
dirs="/mnt /os /iso /wim /installer /nbd /nbd-boot /nbd-efi /root /nix"
regex=$(echo "$dirs" | sed 's, ,|,g')
if mounts=$(mount | grep -Ew "$regex" | awk '{print $3}' | tac); then
for mount in $mounts; do
echo "umount $mount"
umount $mount
done
fi
}
2023-07-18 00:01:07 +08:00
# 可能脚本不是首次运行,先清理之前的残留
clear_previous() {
2024-05-03 23:34:42 +08:00
if is_have_cmd vgchange; then
umount -R /os /nbd || true
vgchange -an
apk add device-mapper
dmsetup remove_all
fi
disconnect_qcow
2024-10-21 00:30:02 +08:00
# 安装 arch 有 gpg-agent 进程驻留
pkill gpg-agent || true
rc-service -q --ifexists --ifstarted nix-daemon stop
swapoff -a
umount_all
# 以下情况 umount -R /1 会提示 busy
# mount /file1 /1
# mount /1/file2 /2
2023-07-18 00:01:07 +08:00
}
2024-10-14 22:41:01 +08:00
# virt-what 自动安装 dmidecode因此同时缓存
cache_dmi_and_virt() {
2024-10-14 22:41:01 +08:00
if ! [ "$_dmi_and_virt_cached" = 1 ]; then
2023-08-25 23:18:23 +08:00
apk add virt-what
# 区分 kvm 和 virtio原因:
# 1. 阿里云 c8y virt-what 不显示 kvm
# 2. 不是所有 kvm 都需要 virtio 驱动,例如 aws nitro
2024-10-14 22:41:01 +08:00
# 3. virt-what 不会检测 virtio
_virt=$(
virt-what
# hyper-v 环境下 modprobe virtio_scsi 也会创建 /sys/bus/virtio/drivers/virtio_scsi
# 因此用 devices 判断更准确,有设备时才有 /sys/bus/virtio/drivers/*
2024-10-14 22:41:01 +08:00
# 或者加上 lspci 检测?
# 不要用 ls /sys/bus/virtio/devices/* && echo virtio
# 因为有可能返回值不为 0 而中断脚本
if ls /sys/bus/virtio/devices/* >/dev/null 2>&1; then
2024-10-14 22:41:01 +08:00
echo virtio
fi
)
_dmi=$(dmidecode | grep -E '(Manufacturer|Asset Tag|Vendor): ' | awk -F': ' '{print $2}')
2024-10-14 22:41:01 +08:00
_dmi_and_virt_cached=1
2023-08-25 23:18:23 +08:00
apk del virt-what
fi
}
2023-08-25 23:18:23 +08:00
is_virt() {
cache_dmi_and_virt
[ -n "$_virt" ]
}
is_virt_contains() {
cache_dmi_and_virt
echo "$_virt" | grep -Eiwq "$1"
}
is_dmi_contains() {
# Manufacturer: Alibaba Cloud
# Manufacturer: Tencent Cloud
# Manufacturer: Huawei Cloud
# Asset Tag: OracleCloud.com
# Vendor: Amazon EC2
# Manufacturer: Amazon EC2
# Asset Tag: Amazon EC2
cache_dmi_and_virt
echo "$_dmi" | grep -Eiwq "$1"
}
2024-09-06 23:00:35 +08:00
2024-10-14 22:41:01 +08:00
cache_lspci() {
if [ -z "$_lspci" ]; then
apk add pciutils
_lspci=$(lspci)
apk del pciutils
fi
}
is_lspci_contains() {
cache_lspci
echo "$_lspci" | grep -Eiwq "$1"
}
2024-10-13 22:58:12 +08:00
get_config() {
cat "/configs/$1"
}
2024-10-18 23:34:57 +08:00
set_config() {
printf '%s' "$2" >"/configs/$1"
}
2024-10-13 22:58:12 +08:00
get_password_linux_sha512() {
get_config password-linux-sha512
}
get_password_windows_administrator_base64() {
get_config password-windows-administrator-base64
}
# debian 安装版、ubuntu 安装版、el/ol 安装版不使用该密码
get_password_plaintext() {
get_config password-plaintext
}
is_password_plaintext() {
get_password_plaintext >/dev/null 2>&1
}
2024-09-06 23:00:35 +08:00
show_netconf() {
grep -r . /dev/netconf/
2023-08-25 23:18:23 +08:00
}
2023-08-25 23:18:23 +08:00
get_ra_to() {
if [ -z "$_ra" ]; then
apk add ndisc6
# 有时会重复收取,所以设置收一份后退出
2023-10-28 21:46:38 +08:00
echo "Gathering network info..."
2023-12-05 21:53:24 +08:00
# shellcheck disable=SC2154
_ra="$(rdisc6 -1 "$ethx")"
2023-08-25 23:18:23 +08:00
apk del ndisc6
2023-11-03 23:55:04 +08:00
# 显示网络配置
2024-09-06 23:00:36 +08:00
info "Network info:"
2023-11-03 23:55:04 +08:00
echo
echo "$_ra" | cat -n
echo
ip addr | cat -n
echo
2024-09-06 23:00:35 +08:00
show_netconf | cat -n
echo
fi
2023-08-25 23:18:23 +08:00
eval "$1='$_ra'"
}
2023-08-25 23:18:23 +08:00
get_netconf_to() {
case "$1" in
2023-10-28 21:46:38 +08:00
slaac | dhcpv6 | rdnss | other) get_ra_to ra ;;
2023-08-25 23:18:23 +08:00
esac
# shellcheck disable=SC2154
# debian initrd 没有 xargs
2023-08-25 23:18:23 +08:00
case "$1" in
2024-05-22 22:32:43 +08:00
slaac) echo "$ra" | grep 'Autonomous address conf' | grep -q Yes && res=1 || res=0 ;;
dhcpv6) echo "$ra" | grep 'Stateful address conf' | grep -q Yes && res=1 || res=0 ;;
rdnss) res=$(echo "$ra" | grep 'Recursive DNS server' | cut -d: -f2-) ;;
2024-05-22 22:32:43 +08:00
other) echo "$ra" | grep 'Stateful other conf' | grep -q Yes && res=1 || res=0 ;;
*) res=$(cat /dev/netconf/$ethx/$1) ;;
2023-08-25 23:18:23 +08:00
esac
eval "$1='$res'"
}
2024-05-08 22:17:33 +08:00
is_ipv4_has_internet() {
grep -q 1 /dev/netconf/*/ipv4_has_internet
2024-05-08 22:17:33 +08:00
}
2023-10-22 19:07:12 +08:00
is_in_china() {
2024-08-19 00:33:59 +08:00
grep -q 1 /dev/netconf/*/is_in_china
2023-10-22 19:07:12 +08:00
}
# 有 dhcpv4 不等于有网关,例如 vultr 纯 ipv6
# 没有 dhcpv4 不等于是静态ip可能是没有 ip
2023-08-25 23:18:23 +08:00
is_dhcpv4() {
get_netconf_to dhcpv4
# shellcheck disable=SC2154
[ "$dhcpv4" = 1 ]
}
is_staticv4() {
if ! is_dhcpv4; then
get_netconf_to ipv4_addr
get_netconf_to ipv4_gateway
if [ -n "$ipv4_addr" ] && [ -n "$ipv4_gateway" ]; then
return 0
fi
fi
return 1
}
is_staticv6() {
if ! is_slaac && ! is_dhcpv6; then
get_netconf_to ipv6_addr
get_netconf_to ipv6_gateway
if [ -n "$ipv6_addr" ] && [ -n "$ipv6_gateway" ]; then
return 0
fi
fi
return 1
}
should_disable_ra_slaac() {
get_netconf_to should_disable_ra_slaac
# shellcheck disable=SC2154
[ "$should_disable_ra_slaac" = 1 ]
}
2023-08-25 23:18:23 +08:00
is_slaac() {
# 防止部分机器slaac/dhcpv6获取的ip/网关无法上网
if should_disable_ra_slaac; then
return 1
fi
2023-08-25 23:18:23 +08:00
get_netconf_to slaac
# shellcheck disable=SC2154
[ "$slaac" = 1 ]
}
2023-08-25 23:18:23 +08:00
is_dhcpv6() {
# 防止部分机器slaac/dhcpv6获取的ip/网关无法上网
if should_disable_ra_slaac; then
return 1
fi
2023-08-25 23:18:23 +08:00
get_netconf_to dhcpv6
2023-08-25 23:18:23 +08:00
# shellcheck disable=SC2154
# 甲骨文即使没有添加 IPv6 地址RA DHCPv6 标志也是开的
# 部分系统开机需要等 DHCPv6 超时
# 这种情况需要禁用 DHCPv6
if [ "$dhcpv6" = 1 ] && ! ip -6 -o addr show scope global dev "$ethx" | grep -q .; then
echo 'DHCPv6 flag is on, but DHCPv6 is not working.'
return 1
fi
2023-08-25 23:18:23 +08:00
[ "$dhcpv6" = 1 ]
}
2023-10-28 21:46:38 +08:00
is_have_ipv6() {
is_slaac || is_dhcpv6 || is_staticv6
}
is_enable_other_flag() {
get_netconf_to other
# shellcheck disable=SC2154
[ "$other" = 1 ]
}
is_have_rdnss() {
# rdnss 可能有几个
2023-10-28 21:46:38 +08:00
get_netconf_to rdnss
[ -n "$rdnss" ]
}
is_windows() {
for dir in /os /wim; do
[ -d $dir/Windows/System32 ] && return 0
done
return 1
}
2024-06-14 23:41:15 +08:00
# 15063 或之后才支持 rdnss
is_windows_support_rdnss() {
apk add pev
for dir in /os /wim; do
dll=$dir/Windows/System32/kernel32.dll
if [ -f $dll ]; then
build_ver="$(peres -v $dll | grep 'Product Version:' | cut -d. -f3)"
echo "Windows Build Version: $build_ver"
2024-06-14 23:41:15 +08:00
apk del pev
[ "$build_ver" -ge 15063 ] && return 0 || return 1
fi
done
2024-06-14 23:41:15 +08:00
error_and_exit "Not found kernel32.dll"
}
is_elts() {
[ -n "$elts" ] && [ "$elts" = 1 ]
}
2024-10-12 23:07:01 +08:00
is_need_change_ssh_port() {
[ -n "$ssh_port" ] && ! [ "$ssh_port" = 22 ]
}
is_need_change_rdp_port() {
[ -n "$rdp_port" ] && ! [ "$rdp_port" = 3389 ]
}
2023-10-28 21:46:38 +08:00
is_need_manual_set_dnsv6() {
# 有没有可能是静态但是有 rdnss
! is_have_ipv6 && return $FALSE
is_dhcpv6 && return $FALSE
is_staticv6 && return $TRUE
is_slaac && ! is_enable_other_flag &&
{ ! is_have_rdnss || { is_have_rdnss && is_windows && ! is_windows_support_rdnss; }; }
2023-10-28 21:46:38 +08:00
}
2024-08-19 00:33:59 +08:00
get_current_dns() {
mark=$(
case "$1" in
4) echo . ;;
6) echo : ;;
esac
)
# debian 11 initrd 没有 xargs awk
# debian 12 initrd 没有 xargs
if false; then
2024-08-19 00:33:59 +08:00
grep '^nameserver' /etc/resolv.conf | awk '{print $2}' | grep -F "$mark"
else
2024-08-19 00:33:59 +08:00
grep '^nameserver' /etc/resolv.conf | cut -d' ' -f2 | grep -F "$mark"
fi
2023-10-28 21:46:38 +08:00
}
to_upper() {
tr '[:lower:]' '[:upper:]'
}
to_lower() {
tr '[:upper:]' '[:lower:]'
}
del_empty_lines() {
sed '/^[[:space:]]*$/d'
}
get_part_num_by_part() {
dev_part=$1
echo "$dev_part" | grep -o '[0-9]*' | tail -1
}
get_fallback_efi_file_name() {
case $(arch) in
x86_64) echo bootx64.efi ;;
aarch64) echo bootaa64.efi ;;
*) error_and_exit ;;
esac
}
del_invalid_efi_entry() {
2024-09-06 23:00:36 +08:00
info "del invalid EFI entry"
apk add lsblk efibootmgr
efibootmgr --quiet --remove-dups
while read -r line; do
part_uuid=$(echo "$line" | awk -F ',' '{print $3}')
efi_index=$(echo "$line" | grep_efi_index)
2024-02-01 01:02:35 +08:00
if ! lsblk -o PARTUUID | grep -q "$part_uuid"; then
echo "Delete invalid EFI Entry: $line"
efibootmgr --quiet --bootnum "$efi_index" --delete-bootnum
fi
done < <(efibootmgr | grep 'HD(.*,GPT,')
}
grep_efi_index() {
awk -F '*' '{print $1}' | sed 's/Boot//'
}
# 某些机器可能不会回落到 bootx64.efi
2024-09-06 23:00:32 +08:00
# 阿里云 ECS 启动项有 EFI Shell
# 添加 bootx64.efi 到最后的话,会进入 EFI Shell
# 因此添加到最前面
add_default_efi_to_nvram() {
2024-09-06 23:00:36 +08:00
info "add default EFI to nvram"
apk add lsblk efibootmgr
if efi_row=$(lsblk /dev/$xda -ro NAME,PARTTYPE,PARTUUID | grep -i "$EFI_UUID"); then
efi_part_uuid=$(echo "$efi_row" | awk '{print $3}')
efi_part_name=$(echo "$efi_row" | awk '{print $1}')
efi_part_num=$(get_part_num_by_part "$efi_part_name")
efi_file=$(get_fallback_efi_file_name)
# 创建条目,先判断是否已经存在
2024-09-06 23:00:32 +08:00
# 好像没必要先判断
if true || ! efibootmgr | grep -i "HD($efi_part_num,GPT,$efi_part_uuid,.*)/File(\\\EFI\\\boot\\\\$efi_file)"; then
efibootmgr --create \
--disk "/dev/$xda" \
--part "$efi_part_num" \
2024-09-06 23:00:32 +08:00
--label "$efi_file" \
--loader "\\EFI\\boot\\$efi_file"
fi
else
2024-07-14 23:00:52 +08:00
# shellcheck disable=SC2154
if [ "$confirmed_no_efi" = 1 ]; then
echo 'Confirmed no EFI in previous step.'
else
# reinstall.sh 里确认过一遍,但是逻辑扇区大于 512 时,可能漏报?
# 这里的应该会根据逻辑扇区来判断?
echo "
Warning: This machine is currently using EFI boot, but the main hard drive does not have an EFI partition.
If this machine supports Legacy BIOS boot (CSM), you can safely restart into the new system by running the reboot command.
If this machine does not support Legacy BIOS boot (CSM), you will not be able to enter the new system after rebooting.
警告:本机目前使用 EFI 引导,但主硬盘没有 EFI 分区。
如果本机支持 Legacy BIOS 引导 (CSM),你可以运行 reboot 命令安全地重启到新系统。
如果本机不支持 Legacy BIOS 引导 (CSM),重启后将无法进入新系统。
"
2024-07-14 23:00:52 +08:00
exit
fi
fi
}
unix2dos() {
target=$1
2023-12-14 00:11:32 +08:00
# 先原地unix2dos出错再用cat可最大限度保留文件权限
if ! command unix2dos $target 2>/tmp/unix2dos.log; then
# 出错后删除 unix2dos 创建的临时文件
2023-12-14 00:11:32 +08:00
rm "$(awk -F: '{print $2}' /tmp/unix2dos.log | xargs)"
tmp=$(mktemp)
cp $target $tmp
command unix2dos $tmp
2023-12-14 00:11:32 +08:00
# cat 可以保留权限
cat $tmp >$target
rm $tmp
fi
}
2023-09-16 20:00:05 +08:00
insert_into_file() {
file=$1
location=$2
regex_to_find=$3
2024-08-19 00:33:59 +08:00
shift 3
# 默认 grep -E
if [ $# -eq 0 ]; then
set -- -E
fi
2023-09-16 20:00:05 +08:00
2023-12-14 00:11:32 +08:00
if [ "$location" = head ]; then
bak=$(mktemp)
cp $file $bak
cat - $bak >$file
else
2024-08-19 00:33:59 +08:00
line_num=$(grep "$@" -n "$regex_to_find" "$file" | cut -d: -f1)
found_count=$(echo "$line_num" | wc -l)
if [ ! "$found_count" -eq 1 ]; then
return 1
fi
2023-09-16 20:00:05 +08:00
case "$location" in
before) line_num=$((line_num - 1)) ;;
after) ;;
*) return 1 ;;
esac
sed -i "${line_num}r /dev/stdin" "$file"
fi
2023-09-16 20:00:05 +08:00
}
get_eths() {
(
cd /dev/netconf
ls
)
}
is_distro_like_debian() {
[ "$distro" = debian ] || [ "$distro" = kali ]
}
create_ifupdown_config() {
conf_file=$1
rm -f $conf_file
if is_distro_like_debian; then
cat <<EOF >>$conf_file
source /etc/network/interfaces.d/*
EOF
fi
# 生成 lo配置
cat <<EOF >>$conf_file
auto lo
iface lo inet loopback
EOF
# ethx
for ethx in $(get_eths); do
mode=auto
enpx=
if is_distro_like_debian; then
if [ -f /etc/network/devhotplug ] && grep -wo "$ethx" /etc/network/devhotplug; then
mode=allow-hotplug
fi
if is_have_cmd udevadm; then
enpx=$(udevadm test-builtin net_id /sys/class/net/$ethx 2>&1 | grep ID_NET_NAME_PATH= | cut -d= -f2)
fi
fi
# dmit debian 普通内核和云内核网卡名不一致,因此需要 rename
# 安装系统时 ens18
# 普通内核 ens18
# 云内核 enp6s18
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=928923
# 头部
{
echo
if [ -n "$enpx" ] && [ "$enpx" != "$ethx" ]; then
2024-08-07 21:46:45 +08:00
echo rename $enpx=$ethx
fi
echo $mode $ethx
} >>$conf_file
# ipv4
if is_dhcpv4; then
echo "iface $ethx inet dhcp" >>$conf_file
elif is_staticv4; then
get_netconf_to ipv4_addr
get_netconf_to ipv4_gateway
cat <<EOF >>$conf_file
iface $ethx inet static
address $ipv4_addr
gateway $ipv4_gateway
EOF
# dns
2024-08-19 00:33:59 +08:00
if list=$(get_current_dns 4); then
for dns in $list; do
cat <<EOF >>$conf_file
dns-nameservers $dns
EOF
done
fi
fi
# ipv6
if is_slaac; then
echo "iface $ethx inet6 auto" >>$conf_file
elif is_dhcpv6; then
echo "iface $ethx inet6 dhcp" >>$conf_file
elif is_staticv6; then
get_netconf_to ipv6_addr
get_netconf_to ipv6_gateway
cat <<EOF >>$conf_file
iface $ethx inet6 static
address $ipv6_addr
gateway $ipv6_gateway
EOF
fi
# dns
# 有 ipv6 但需设置 dns 的情况
2024-08-19 00:33:59 +08:00
if is_need_manual_set_dnsv6; then
for dns in $(get_current_dns 6); do
cat <<EOF >>$conf_file
dns-nameserver $dns
EOF
done
fi
# 禁用 ra
if should_disable_ra_slaac; then
if [ "$distro" = alpine ]; then
cat <<EOF >>$conf_file
pre-up echo 0 >/proc/sys/net/ipv6/conf/$ethx/accept_ra
EOF
else
cat <<EOF >>$conf_file
accept_ra 0
EOF
fi
fi
done
}
2024-08-19 00:42:12 +08:00
space_to_newline() {
sed 's/ /\n/g'
}
trim() {
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
quote_word() {
sed -E 's/([^[:space:]]+)/"\1"/g'
}
quote_line() {
awk '{print "\""$0"\""}'
}
add_space() {
space_count=$1
spaces=$(printf '%*s' "$space_count" '')
sed "s/^/$spaces/"
}
# 不够严谨,谨慎使用
nix_replace() {
local key=$1
local value=$2
local type=$3
local file=$4
local key_ value_
key_=$(echo "$key" | sed 's \. \\\. g') # . 改成 \.
if [ "$type" = array ]; then
local value_="[ $value ]"
fi
sed -i "s/$key_ =.*/$key = $value_;/" "$file"
}
create_nixos_network_config() {
conf_file=$1
true >$conf_file
# 头部
cat <<EOF >>$conf_file
networking = {
usePredictableInterfaceNames = false;
EOF
for ethx in $(get_eths); do
# ipv4
if is_staticv4; then
get_netconf_to ipv4_addr
get_netconf_to ipv4_gateway
IFS=/ read -r address prefix < <(echo "$ipv4_addr")
cat <<EOF >>$conf_file
interfaces.$ethx.ipv4.addresses = [
{
address = "$address";
prefixLength = $prefix;
}
];
defaultGateway = {
address = "$ipv4_gateway";
interface = "$ethx";
};
EOF
fi
# ipv6
if is_staticv6; then
get_netconf_to ipv6_addr
get_netconf_to ipv6_gateway
IFS=/ read -r address prefix < <(echo "$ipv6_addr")
cat <<EOF >>$conf_file
interfaces.$ethx.ipv6.addresses = [
{
address = "$address";
prefixLength = $prefix;
}
];
2024-08-20 00:27:52 +08:00
defaultGateway6 = {
address = "$ipv6_gateway";
interface = "$ethx";
2024-08-19 00:42:12 +08:00
};
EOF
fi
done
# 全局 dns
need_set_dns=false
for ethx in $(get_eths); do
if is_staticv4 || is_staticv6 || is_need_manual_set_dnsv6; then
need_set_dns=true
break
fi
done
if $need_set_dns; then
cat <<EOF >>$conf_file
nameservers = [
$(get_current_dns | quote_line | add_space 4)
];
EOF
fi
# 尾部
cat <<EOF >>$conf_file
};
EOF
# nixos 默认网络管理器是 dhcpcd
# 但配置静态 ip 时用的是脚本
# /nix/store/qcr1xxjdxcrnwqwrgysqpxx2aibp9fdl-unit-script-network-addresses-eth0-start/bin/network-addresses-eth0-start
# ...
# if out=$(ip addr replace "181.x.x.x/24" dev "eth0" 2>&1); then
# echo "done"
# else
# echo "'ip addr replace "181.x.x.x/24" dev "eth0"' failed: $out"
# exit 1
# fi
# ...
# 禁用 ra
for ethx in $(get_eths); do
if should_disable_ra_slaac; then
mode=1
if [ "$mode" = 1 ]; then
cat <<EOF >>$conf_file
boot.kernel.sysctl."net.ipv6.conf.$ethx.accept_ra" = false;
EOF
elif [ "$mode" = 2 ]; then
# nixos 配置静态 ip 时用的是脚本
# 好像因此不起作用
cat <<EOF >>$conf_file
networking.dhcpcd.extraConfig =
''
interface $ethx
ipv6ra_noautoconf
'';
EOF
elif [ "$mode" = 3 ]; then
# 暂时没用到 networkd
cat <<EOF >>$conf_file
systemd.network.networks.$ethx = {
matchConfig.Name = "$ethx";
networkConfig = {
IPv6AcceptRA = false;
};
};
EOF
fi
fi
done
}
2023-08-25 23:18:23 +08:00
install_alpine() {
2024-09-06 23:00:36 +08:00
info "install alpine"
2024-12-06 22:50:05 +08:00
need_ram=512
swap_size=$(get_need_swap_size $need_ram)
[ "$swap_size" -gt 0 ] && hack_lowram=true || hack_lowram=false
2023-12-02 23:30:39 +08:00
2024-12-06 22:50:05 +08:00
# alpine 安装时会自动检测安装需要的 firmware
# https://github.com/alpinelinux/alpine-conf/blob/3.18.1/setup-disk.in#L421
# 但如果没有 modloop 则无法检测
# 所以删除 modloop 前先记录用到的 firmware 包
fw_pkgs=$(get_alpine_firmware_pkgs)
if $hack_lowram; then
2023-09-21 00:12:49 +08:00
# 预先加载需要的模块
if rc-service -q modloop status; then
modules="ext4 vfat nls_utf8 nls_cp437"
2023-09-21 00:12:49 +08:00
for mod in $modules; do
modprobe $mod
done
# crc32c 等于 crc32c-intel
# 没有 sse4.2 的机器加载 crc32c 时会报错 modprobe: ERROR: could not insert 'crc32c_intel': No such device
modprobe crc32c || modprobe crc32c-generic
2023-09-21 00:12:49 +08:00
fi
# 删除 modloop ,释放内存
ensure_service_stopped modloop
2023-09-21 00:12:49 +08:00
rm -f /lib/modloop-lts /lib/modloop-virt
2023-12-02 23:30:39 +08:00
fi
2023-09-21 00:12:49 +08:00
2023-12-02 23:30:39 +08:00
# bios机器用 setup-disk 自动分区会有 boot 分区
# 因此手动分区安装
create_part
2024-08-19 00:33:59 +08:00
mount_part_basic_layout /os /os/boot/efi
2023-09-21 00:12:49 +08:00
2023-12-02 23:30:39 +08:00
# 创建 swap
2024-12-06 22:50:05 +08:00
if $hack_lowram; then
create_swap $swap_size /os/swapfile
fi
# 网络配置
create_ifupdown_config /etc/network/interfaces
2023-08-25 23:18:23 +08:00
echo
cat -n /etc/network/interfaces
2023-08-25 23:18:23 +08:00
echo
2023-05-13 00:14:46 +08:00
# 在 arm netboot initramfs init 中
# 如果识别到rtc硬件就往系统添加hwclock服务否则添加swclock
# 这个设置也被复制到安装的系统中
# 但是从initramfs chroot到真正的系统后是能识别rtc硬件的
# 所以我们手动改用hwclock修复这个问题
2023-06-18 21:27:22 +08:00
rc-update del swclock boot || true
2023-08-25 23:18:23 +08:00
rc-update add hwclock boot
2023-05-13 00:14:46 +08:00
# 通过 setup-alpine 安装会启用以下几个服务
2024-12-06 22:50:05 +08:00
# https://github.com/alpinelinux/alpine-conf/blob/3.18.1/setup-alpine.in#L229
# boot
rc-update add networking boot
rc-update add seedrng boot
# default
rc-update add crond
2023-08-25 23:18:23 +08:00
if [ -e /dev/input/event0 ]; then
rc-update add acpid
fi
2023-11-28 23:59:17 +08:00
2023-08-25 23:18:23 +08:00
# 如果是 vm 就用 virt 内核
if is_virt; then
2023-09-03 19:35:01 +08:00
kernel_flavor="virt"
2023-08-25 23:36:32 +08:00
else
2023-09-03 19:35:01 +08:00
kernel_flavor="lts"
2023-08-25 23:18:23 +08:00
fi
2023-05-13 00:14:46 +08:00
# 重置为官方仓库配置
2023-10-31 22:33:59 +08:00
# 国内机可能无法访问mirror列表而报错
if false; then
true >/etc/apk/repositories
setup-apkrepos -1
fi
2023-05-13 00:14:46 +08:00
# setup-disk 安装 grub 跳过了添加引导项到 nvram
# 防止部分机器不会 fallback 到 bootx64.efi
if is_efi; then
apk add efibootmgr
sed -i 's/--no-nvram//' "$(which setup-disk)"
fi
2023-05-13 00:14:46 +08:00
# 安装到硬盘
# alpine默认使用 syslinux (efi 环境除外),这里强制使用 grub方便用脚本再次重装
2023-09-03 19:35:01 +08:00
KERNELOPTS="$(get_ttys console=)"
export KERNELOPTS
2023-05-13 00:14:46 +08:00
export BOOTLOADER="grub"
setup-disk -m sys -k $kernel_flavor /os
# 删除 setup-disk 时自动安装的包
apk del e2fsprogs dosfstools efibootmgr grub*
# 安装到硬盘后才安装各种应用
# 避免占用 Live OS 内存
# 网络
2024-11-29 22:13:53 +08:00
# udhcpc
# 坑1 ip -4 addr 无法知道是否是 dhcp
# 坑2 networking 服务不会运行 udhcpc6
# 坑3 h3c 移动云电脑 udhcpc6 无法获取 dhcpv6
# dhcpcd
# 坑1 slaac默认开了隐私保护造成ip和后台面板不一致
# slaac方案1: udhcpc + rdnssd
# slaac方案2: dhcpcd + 关闭隐私保护
# dhcpv6方案: dhcpcd
# 综合使用dhcpcd方案
# 1 无需改动/etc/network/interfaces自动根据ra使用slaac和dhcpv6
# 2 自带rdnss支持
# 3 唯一要做的是关闭隐私保护
# 安装 dhcpcd
chroot /os apk add dhcpcd
chroot /os sed -i '/^slaac private/s/^/#/' /etc/dhcpcd.conf
chroot /os sed -i '/^#slaac hwaddr/s/^#//' /etc/dhcpcd.conf
# 安装其他部件
chroot /os setup-keymap us us
chroot /os setup-timezone -i Asia/Shanghai
chroot /os setup-ntp chrony || true
2023-12-02 23:30:39 +08:00
# 安装固件微码会触发 grub-probe
# 如果没挂载会报错
# Executing grub-2.12-r5.trigger
# /usr/sbin/grub-probe: error: failed to get canonical path of `/dev/vda1'.
# ERROR: grub-2.12-r5.trigger: script exited with error 1
mount_pseudo_fs /os
# setup-disk 会自动选择固件,但不包括微码?
2024-12-06 22:50:05 +08:00
# https://github.com/alpinelinux/alpine-conf/blob/3.18.1/setup-disk.in#L421
if fw_pkgs="$fw_pkgs $(get_ucode_firmware_pkgs)" && [ -n "$fw_pkgs" ]; then
chroot /os apk add $fw_pkgs
fi
2023-12-07 23:06:15 +08:00
# 3.19 或以上,非 efi 需要手动安装 grub
if ! is_efi; then
chroot /os grub-install --target=i386-pc /dev/$xda
2023-12-07 23:06:15 +08:00
fi
# efi grub 添加 fwsetup 条目
chroot /os update-grub
2024-04-28 00:02:31 +08:00
2023-12-02 23:30:39 +08:00
# 是否保留 swap
if [ -e /os/swapfile ]; then
if false; then
echo "/swapfile swap swap defaults 0 0" >>/os/etc/fstab
ln -sf /etc/init.d/swap /os/etc/runlevels/boot/swap
else
swapoff -a
rm /os/swapfile
fi
fi
2023-07-25 00:21:08 +08:00
}
2023-06-04 19:12:57 +08:00
2024-03-31 00:32:05 +08:00
get_cpu_vendor() {
cpu_vendor=$(grep 'vendor_id' /proc/cpuinfo | head -1 | awk '{print $NF}')
2024-03-31 00:32:05 +08:00
case "$cpu_vendor" in
GenuineIntel) echo intel ;;
AuthenticAMD) echo amd ;;
*) echo other ;;
esac
}
2024-03-28 00:16:05 +08:00
2024-08-19 00:33:59 +08:00
min() {
printf "%d\n" "$@" | sort -n | head -n 1
}
# 设置线程
# 根据 cpu 核数,每个线程的内存,取最小值
get_build_threads() {
threads_per_mb=$1
2024-10-01 20:05:18 +08:00
threads_by_core=$(nproc)
2024-08-19 00:33:59 +08:00
threads_by_ram=$(($(get_approximate_ram_size) / threads_per_mb))
[ $threads_by_ram -eq 0 ] && threads_by_ram=1
min $threads_by_ram $threads_by_core
}
2024-08-20 00:27:52 +08:00
add_newline() {
# shellcheck disable=SC1003
case "$1" in
head | start) sed -e '1s/^/\n/' ;;
tail | end) sed -e '$a\\' ;;
both) sed -e '1s/^/\n/' -e '$a\\' ;;
esac
}
2024-08-19 00:42:12 +08:00
install_nixos() {
2024-09-06 23:00:36 +08:00
info "Install NixOS"
2024-08-19 00:42:12 +08:00
os_dir=/os
keep_swap=true
nix_from=website
ram_per_thread=2048
threads=$(get_build_threads $ram_per_thread)
swap_size=$(get_need_swap_size $ram_per_thread)
2024-08-19 00:42:12 +08:00
2024-08-20 00:27:52 +08:00
show_nixos_config() {
echo
cat -n /os/etc/nixos/configuration.nix
echo
cat -n /os/etc/nixos/hardware-configuration.nix
echo
}
2024-08-19 00:42:12 +08:00
# 挂载分区,创建 swapfile
mount_part_basic_layout /os /os/efi
if [ "$swap_size" -gt 0 ]; then
create_swap "$swap_size" /os/swapfile
fi
2024-08-19 00:42:12 +08:00
# 步骤
# 1. 安装 nix (nix-xxx)
# 2. 用 nix 安装 nixos-install-tools (nixos-xxx)
# 3. 运行 nixos-generate-config 生成配置 + 编辑
# 4. 运行 nixos-install
2024-09-06 23:00:35 +08:00
# https://nixos.org/manual/nixos/stable/index.html#sec-installing-from-other-distro
2024-08-19 00:42:12 +08:00
# nix 安装方式 分支 版本
# apk add nix 3.20 2.22.0 # nix 本体跟 alpine 正常的软件一样,不在 /nix/store 里面
2024-08-19 00:42:12 +08:00
# env -iA nixpkgs.nix 24.05 2.18.5
# sh <(curl -L https://nixos.org/nix/install) unstable? 2.24.2
# apk add 安装的 nix 有时会卡在
# copying path '/nix/store/gcbrjlfm5h21ybf1h2lfq773zafjmzjr-curl-8.7.1-man' from 'https://cache.nixos.org'...
# 但是 cpu 空载
2024-08-19 00:42:12 +08:00
# 安装 nix
mkdir -p /os/nix /nix
mount --bind /os/nix /nix
# nix 安装脚本和 /root/.nix-profile/etc/profile.d/nix.sh 都会用到这两个变量
# 但从 alpine local.d 运行没有这两个变量
export USER=root
export HOME=/root
case "$nix_from" in
alpine)
apk add nix
# 设置 nix 镜像和线程
# alpine 默认设置了 4 线程
# https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/community/nix/APKBUILD#L125
sed -i '/max-jobs/d' /etc/nix/nix.conf
echo "max-jobs = $threads" >>/etc/nix/nix.conf
if is_in_china; then
echo "substituters = $mirror/store" >>/etc/nix/nix.conf
fi
rc-service -q nix-daemon restart
# 添加 nix-env 安装的软件到 PATH
PATH="/root/.nix-profile/bin:$PATH"
;;
website)
# https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/community/nix/nix.pre-install
# https://nix.dev/manual/nix/latest/installation/multi-user
if ! grep -q nixbld /etc/passwd; then
addgroup -S nixbld
for n in $(seq 1 10); do
adduser -S -D -H -h /var/empty -s /sbin/nologin -G nixbld \
-g "Nix build user $n" nixbld$n
done
fi
2024-08-19 00:42:12 +08:00
if is_in_china; then
sh=https://mirror.nju.edu.cn/nix/latest/install
else
sh=https://nixos.org/nix/install
fi
apk add xz
wget -O- "$sh" | sh -s -- --no-channel-add
apk del xz
# shellcheck source=/dev/null
. /root/.nix-profile/etc/profile.d/nix.sh
;;
esac
2024-08-19 00:42:12 +08:00
# 添加 channel
# shellcheck disable=SC2154
nix-channel --add $mirror/nixos-$releasever nixpkgs
nix-channel --update
# 安装 channal 的 nix
# shellcheck source=/dev/null
if false; then
nix-env -iA nixpkgs.nix -j $threads
2024-08-19 00:42:12 +08:00
. ~/.nix-profile/etc/profile.d/nix.sh
fi
# 安装 nixos-install-tools
nix-env -iA nixpkgs.nixos-install-tools -j $threads
2024-08-19 00:42:12 +08:00
2024-08-20 00:27:52 +08:00
# 生成配置并显示
2024-08-19 00:42:12 +08:00
nixos-generate-config --root /os
2024-08-20 00:27:52 +08:00
echo "Original NixOS Configuration:"
show_nixos_config
2024-08-19 00:42:12 +08:00
2024-08-20 00:27:52 +08:00
# 修改 configuration.nix
2024-08-19 00:42:12 +08:00
if is_efi; then
nix_bootloader="boot.loader.efi.efiSysMountPoint = \"/efi\";"
else
nix_bootloader="boot.loader.grub.device = \"/dev/$xda\";"
fi
if is_in_china; then
nix_substituters="nix.settings.substituters = lib.mkForce [ \"$mirror/store\" ];"
fi
if [ -e /os/swapfile ] && $keep_swap; then
nix_swap="swapDevices = [{ device = \"/swapfile\"; size = $swap_size; }];"
fi
2024-10-12 23:07:01 +08:00
if is_need_change_ssh_port; then
nix_ssh_ports="services.openssh.ports = [ $ssh_port ];"
fi
2024-08-19 00:42:12 +08:00
# TODO: 准确匹配网卡,添加 udev 或者直接配置 networkd 匹配 mac
create_nixos_network_config /tmp/nixos_network_config.nix
2024-11-29 22:13:53 +08:00
del_empty_lines <<EOF | add_space 2 | add_newline both |
2024-08-19 00:42:12 +08:00
############### Add by reinstall.sh ###############
$nix_bootloader
$nix_swap
$nix_substituters
boot.kernelParams = [ $(get_ttys console= | quote_word) ];
services.openssh.enable = true;
services.openssh.settings.PermitRootLogin = "yes";
2024-10-12 23:07:01 +08:00
$nix_ssh_ports
2024-08-19 00:42:12 +08:00
$(cat /tmp/nixos_network_config.nix)
###################################################
EOF
2024-08-20 00:27:52 +08:00
insert_into_file /os/etc/nixos/configuration.nix before "networking.hostName" -F
2024-08-19 00:42:12 +08:00
2024-08-20 00:27:52 +08:00
# 修改 hardware-configuration.nix
2024-08-19 00:42:12 +08:00
# 在 vultr efi 机器上nixos-generate-config 不会添加 virtio_pci
# 导致 virtio_blk 用不了,启动时 initrd 找不到系统分区
2024-08-20 00:27:52 +08:00
# 可能由于 alpine 的 virtio_pci 编译进内核而不是模块
# 因此 nixos-generate-config 不会添加 virtio_pci 到配置文件
2024-08-19 00:42:12 +08:00
olds=$(
grep -F 'boot.initrd.availableKernelModules' /os/etc/nixos/hardware-configuration.nix |
cut -d= -f2 | tr -d '"[];' | xargs
)
alls="$olds"
2024-08-20 00:27:52 +08:00
# https://github.com/search?q=repo%3ANixOS%2Fnixpkgs+availableKernelModules&type=code
for mod in ahci ata_piix uhci_hcd sr_mod nvme \
2024-08-19 00:42:12 +08:00
virtio_pci virtio_blk virtio_scsi \
xen_blkfront xen_scsifront \
hv_storvsc \
vmw_pvscsi \
mptspi; do
if [ -d /sys/module/$mod ] && ! echo "$olds" | grep -wq "$mod"; then
echo "Adding modules: $mod"
alls="$alls $mod"
fi
done
# 去除多余的空格
alls=$(echo "$alls" | xargs)
# boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ];
nix_replace \
boot.initrd.availableKernelModules \
"$(echo "$alls" | quote_word)" \
array \
/os/etc/nixos/hardware-configuration.nix
2024-08-20 00:27:52 +08:00
# 显示修改后的配置
echo "Modified NixOS Configuration:"
show_nixos_config
2024-08-19 00:42:12 +08:00
# 安装系统
nixos-install --root /os --no-root-passwd -j $threads
2024-08-19 00:42:12 +08:00
# 设置密码
2024-10-13 22:58:12 +08:00
echo "root:$(get_password_linux_sha512)" | nixos-enter --root /os -- \
/run/current-system/sw/bin/chpasswd -e
2024-08-19 00:42:12 +08:00
# 设置 channel
if is_in_china; then
nixos-enter --root /os -- \
/run/current-system/sw/bin/nix-channel \
--add https://mirrors.cernet.edu.cn/nix-channels/nixos-$releasever nixos
fi
# 清理
nix-env -e '*'
# /nix/var/nix/profiles/system/sw/bin/nix-collect-garbage -d
2024-08-20 00:27:52 +08:00
/nix/var/nix/profiles/system/sw/bin/nixos-enter --root /os -- \
/run/current-system/sw/bin/nix-collect-garbage -d
2024-08-19 00:42:12 +08:00
# 删除 nix
umount /nix
apk del nix
# swapfile
if [ -e /os/swapfile ]; then
if $keep_swap; then
:
else
swapoff -a
rm -rf /os/swapfile
fi
fi
2024-08-20 00:27:52 +08:00
# 重新显示配置,方便查看
show_nixos_config
2024-08-19 00:42:12 +08:00
}
2024-03-31 00:32:05 +08:00
install_arch_gentoo() {
2024-09-06 23:00:36 +08:00
info "install $distro"
2024-03-31 00:32:05 +08:00
set_locale() {
echo "C.UTF-8 UTF-8" >>$os_dir/etc/locale.gen
chroot $os_dir locale-gen
}
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# shellcheck disable=SC2317
install_arch() {
# 添加 swap
create_swap_if_ram_less_than 1024 $os_dir/swapfile
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
apk add arch-install-scripts
2024-03-28 00:16:05 +08:00
2024-10-21 00:30:02 +08:00
# 为了二次运行时 /etc/pacman.conf 未修改
if [ -f /etc/pacman.conf.orig ]; then
cp /etc/pacman.conf.orig /etc/pacman.conf
else
cp /etc/pacman.conf /etc/pacman.conf.orig
fi
2024-03-31 00:32:05 +08:00
# 设置 repo
insert_into_file /etc/pacman.conf before '\[core\]' <<EOF
SigLevel = Never
ParallelDownloads = 5
EOF
cat <<EOF >>/etc/pacman.conf
[core]
Include = /etc/pacman.d/mirrorlist
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
[extra]
Include = /etc/pacman.d/mirrorlist
EOF
mkdir -p /etc/pacman.d
# shellcheck disable=SC2016
case "$(uname -m)" in
x86_64) dir='$repo/os/$arch' ;;
aarch64) dir='$arch/$repo' ;;
esac
# shellcheck disable=SC2154
echo "Server = $mirror/$dir" >/etc/pacman.d/mirrorlist
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 安装系统
# 要安装分区工具(包含 fsck.xxx),用于 initramfs 检查分区数据
# base 包含 e2fsprogs
pkgs="base grub openssh"
if is_efi; then
pkgs="$pkgs efibootmgr dosfstools"
fi
if [ "$(uname -m)" = aarch64 ]; then
pkgs="$pkgs archlinuxarm-keyring"
fi
pacstrap -K $os_dir $pkgs
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# dns
2024-05-22 22:32:43 +08:00
cp_resolv_conf $os_dir
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 挂载伪文件系统
mount_pseudo_fs $os_dir
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 要先设置语言,再安装内核,不然出现
# ==> Creating gzip-compressed initcpio image: '/boot/initramfs-linux.img'
# bsdtar: bsdtar: Failed to set default locale
# Failed to set default locale
set_locale
if [ "$(uname -m)" = aarch64 ]; then
chroot $os_dir pacman-key --lsign-key builder@archlinuxarm.org
fi
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# firmware + microcode
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot $os_dir pacman -Syu --noconfirm $fw_pkgs
2024-03-28 00:16:05 +08:00
fi
2024-03-31 00:32:05 +08:00
# arm 的内核有多种选择,默认是 linux-aarch64所以要添加 --noconfirm
chroot $os_dir pacman -Syu --noconfirm linux
}
# shellcheck disable=SC2317
install_gentoo() {
# 添加 swap
create_swap_if_ram_less_than 2048 $os_dir/swapfile
# 解压系统
apk add tar xz
# shellcheck disable=SC2154
download "$img" $os_dir/gentoo.tar.xz
echo "Uncompressing Gentoo..."
tar xpf $os_dir/gentoo.tar.xz -C $os_dir --xattrs-include='*.*' --numeric-owner
rm $os_dir/gentoo.tar.xz
apk del tar xz
# dns
2024-05-22 22:32:43 +08:00
cp_resolv_conf $os_dir
2024-03-31 00:32:05 +08:00
# 挂载伪文件系统
mount_pseudo_fs $os_dir
# 下载仓库,选择 profile
chroot $os_dir emerge-webrsync
profile=$(
# 筛选 stable systemd再选择最短的
if false; then
chroot $os_dir eselect profile list | grep stable | grep systemd |
awk '(NR == 1 || length($2) < length(shortest)) { shortest = $2 } END { print shortest }'
else
chroot $os_dir eselect profile list | grep stable | grep systemd |
awk '{print length($2), $2}' | sort -n | head -1 | awk '{print $2}'
fi
)
echo "Select profile: $profile"
chroot $os_dir eselect profile set $profile
# 设置 license
cat <<EOF >>$os_dir/etc/portage/make.conf
ACCEPT_LICENSE="*"
EOF
cat <<EOF >>$os_dir/etc/portage/make.conf
2024-08-19 00:33:59 +08:00
MAKEOPTS="-j$(get_build_threads 2048)"
2024-03-28 00:16:05 +08:00
EOF
2024-03-31 00:32:05 +08:00
# 设置 http repo + binpkg repo
# https://mirror.nju.edu.cn/gentoo/releases/amd64/autobuilds/current-stage3-amd64-systemd-mergedusr/stage3-amd64-systemd-mergedusr-20240317T170433Z.tar.xz
2024-03-31 00:32:05 +08:00
mirror_short=$(echo "$img" | sed 's,/releases/.*,,')
mirror_long=$(echo "$img" | sed 's,/autobuilds/.*,,')
profile_ver=$(chroot $os_dir eselect profile show | grep -Eo '/[0-9.]*/' | cut -d/ -f2)
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
if [ "$(uname -m)" = x86_64 ]; then
if chroot $os_dir ld.so --help | grep supported | grep -q x86-64-v3; then
binpkg_type=x86-64-v3
else
binpkg_type=x86-64
fi
2024-03-28 00:16:05 +08:00
else
2024-03-31 00:32:05 +08:00
binpkg_type=arm64
2024-03-28 00:16:05 +08:00
fi
2024-03-31 00:32:05 +08:00
cat <<EOF >>$os_dir/etc/portage/make.conf
2024-03-28 00:16:05 +08:00
GENTOO_MIRRORS="$mirror_short"
FEATURES="getbinpkg"
EOF
2024-03-31 00:32:05 +08:00
cat <<EOF >$os_dir/etc/portage/binrepos.conf/gentoobinhost.conf
2024-03-28 00:16:05 +08:00
[binhost]
priority = 9999
sync-uri = $mirror_long/binpackages/$profile_ver/$binpkg_type
EOF
2024-03-31 00:32:05 +08:00
# 下载公钥
chroot $os_dir getuto
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
set_locale
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 安装 git 会升级 glibc此时 /etc/locale.gen 不能为空,否则会提示生成所有 locale
# Generating all locales; edit /etc/locale.gen to save time/space
chroot $os_dir emerge dev-vcs/git
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 设置 git repo
if is_in_china; then
git_uri=https://mirror.nju.edu.cn/git/gentoo-portage.git
2024-03-31 00:32:05 +08:00
else
# github 不支持 ipv6
2024-05-08 22:17:33 +08:00
is_ipv4_has_internet && git_uri=https://github.com/gentoo-mirror/gentoo.git ||
git_uri=https://anongit.gentoo.org/git/repo/gentoo.git
2024-03-31 00:32:05 +08:00
fi
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
mkdir -p $os_dir/etc/portage/repos.conf
cat <<EOF >$os_dir/etc/portage/repos.conf/gentoo.conf
2024-03-28 00:16:05 +08:00
[gentoo]
location = /var/db/repos/gentoo
sync-type = git
sync-uri = $git_uri
EOF
2024-03-31 00:32:05 +08:00
rm -rf $os_dir/var/db/repos/gentoo
chroot $os_dir emerge --sync
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
if [ "$(uname -m)" = x86_64 ]; then
# https://packages.gentoo.org/packages/sys-block/io-scheduler-udev-rules
chroot $os_dir emerge sys-block/io-scheduler-udev-rules
fi
if is_efi; then
chroot $os_dir emerge sys-fs/dosfstools
fi
# firmware + microcode
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot $os_dir emerge $fw_pkgs
2024-03-31 00:32:05 +08:00
fi
# 安装 grub + 内核
# TODO: 先判断是否有 binpkg有的话不修改 GRUB_PLATFORMS
is_efi && grub_platforms="efi-64" || grub_platforms="pc"
echo GRUB_PLATFORMS=\"$grub_platforms\" >>$os_dir/etc/portage/make.conf
echo "sys-kernel/installkernel dracut grub" >$os_dir/etc/portage/package.use/installkernel
chroot $os_dir emerge sys-kernel/gentoo-kernel-bin
}
os_dir=/os
# 挂载分区
2024-08-19 00:33:59 +08:00
mount_part_basic_layout /os /os/efi
2024-03-31 00:32:05 +08:00
2024-08-19 00:33:59 +08:00
# 安装系统
2024-03-31 00:32:05 +08:00
install_$distro
# 安装 arch 有 gpg-agent 进程驻留
pkill gpg-agent || true
2024-03-31 00:32:05 +08:00
# 初始化
2024-10-21 00:30:02 +08:00
if false; then
# preset-all 后多了很多服务内存占用多了几十M
chroot $os_dir systemctl preset-all
fi
# 此时不能用
# chroot $os_dir timedatectl set-timezone Asia/Shanghai
2024-03-31 00:32:05 +08:00
chroot $os_dir systemd-firstboot --force --timezone=Asia/Shanghai
# gentoo 不会自动创建 machine-id
clear_machine_id $os_dir
2024-03-28 00:16:05 +08:00
chroot $os_dir systemctl enable systemd-networkd
chroot $os_dir systemctl enable systemd-resolved
chroot $os_dir systemctl enable sshd
allow_root_password_login $os_dir
2024-10-12 23:07:01 +08:00
if is_need_change_ssh_port; then
change_ssh_port $os_dir $ssh_port
fi
2024-03-28 00:16:05 +08:00
2024-03-31 00:32:05 +08:00
# 修改密码
change_root_password $os_dir
2024-03-31 00:32:05 +08:00
# 网络配置
apk add cloud-init
2024-10-21 00:30:02 +08:00
# 第二次运行会报错
useradd systemd-network || true
2024-03-31 00:32:05 +08:00
create_cloud_init_network_config net.cfg
# 正常应该是 -D gentoo但 alpine 的 cloud-init 包缺少 gentoo 配置
cloud-init devel net-convert -p net.cfg -k yaml -d out -D alpine -O networkd
cp out/etc/systemd/network/10-cloud-init-eth*.network $os_dir/etc/systemd/network/
rm -rf out
# 删除网卡名匹配
sed -i '/^Name=/d' $os_dir/etc/systemd/network/10-cloud-init-eth*.network
rm -rf net.cfg
apk del cloud-init
# arch gentoo 网络配置是用 alpine cloud-init 生成的
# cloud-init 版本够新,因此无需修复 onlink 网关
2024-03-31 00:32:05 +08:00
2024-03-28 00:16:05 +08:00
# ntp 用 systemd 自带的
2024-03-31 00:32:05 +08:00
# TODO: vm agent + 随机数生成器
# grub
2024-03-28 00:16:05 +08:00
if is_efi; then
2024-03-31 00:32:05 +08:00
# arch gentoo 推荐 efi 挂载在 /efi
chroot $os_dir grub-install --efi-directory=/efi
chroot $os_dir grub-install --efi-directory=/efi --removable
2024-03-28 00:16:05 +08:00
else
chroot $os_dir grub-install /dev/$xda
fi
2024-03-31 00:32:05 +08:00
# cmdline + 生成 grub.cfg
2024-03-28 00:16:05 +08:00
if [ -d $os_dir/etc/default/grub.d ]; then
file=$os_dir/etc/default/grub.d/cmdline.conf
else
file=$os_dir/etc/default/grub
fi
ttys_cmdline=$(get_ttys console=)
echo GRUB_CMDLINE_LINUX=\"$ttys_cmdline\" >>$file
chroot $os_dir grub-mkconfig -o /boot/grub/grub.cfg
# fstab
2024-03-31 00:32:05 +08:00
# fstab 可不写 efi 条目, systemd automount 会自动挂载
2024-03-28 00:16:05 +08:00
apk add arch-install-scripts
genfstab -U $os_dir | sed '/swap/d' >$os_dir/etc/fstab
apk del arch-install-scripts
# 删除 resolv.conf不然 systemd-resolved 无法创建软链接
2024-05-22 22:32:43 +08:00
rm_resolv_conf $os_dir
2024-03-28 00:16:05 +08:00
# 删除 swap
swapoff -a
rm -rf $os_dir/swapfile
}
2024-09-06 23:00:33 +08:00
get_http_file_size() {
url=$1
# 网址重定向可能得到多个 Content-Length, 选最后一个
wget --spider -S "$url" 2>&1 | grep 'Content-Length:' |
tail -1 | awk '{print $2}' | grep .
}
2023-10-22 19:07:12 +08:00
2024-09-06 23:00:32 +08:00
pipe_extract() {
# alpine busybox 自带 gzip但官方版也许性能更好
case "$img_type_warp" in
xz | gzip | zstd)
apk add $img_type_warp
"$img_type_warp" -dc
;;
tar)
apk add tar
tar x -O
;;
tar.*)
type=$(echo "$img_type_warp" | cut -d. -f2)
apk add tar "$type"
tar x "--$type" -O
;;
'') cat ;;
2024-09-06 23:00:32 +08:00
*) error_and_exit "Not supported img_type_warp: $img_type_warp" ;;
esac
2023-10-22 19:07:12 +08:00
}
dd_raw_with_extract() {
info "dd raw"
2024-09-06 23:00:36 +08:00
2024-09-06 23:00:32 +08:00
# 用官方 wget一来带进度条二来自带重试功能
apk add wget
2023-06-04 19:12:57 +08:00
2024-09-06 23:00:32 +08:00
if ! wget $img -O- | pipe_extract >/dev/$xda 2>/tmp/dd_stderr; then
# vhd 文件结尾有 512 字节额外信息,可以忽略
if grep -iq 'No space' /tmp/dd_stderr; then
apk add parted
2024-10-18 23:58:03 +08:00
disk_size=$(get_disk_size /dev/$xda)
disk_end=$((disk_size - 1))
2024-10-18 23:58:03 +08:00
# 如果报错,那大概是因为镜像比硬盘大
if last_part_end=$(parted -sf /dev/$xda 'unit b print' ---pretend-input-tty |
del_empty_lines | tail -1 | awk '{print $3}' | sed 's/B//' | grep .); then
echo "Last part end: $last_part_end"
echo "Disk end: $disk_end"
if [ "$last_part_end" -le "$disk_end" ]; then
echo "Safely ignore no space error."
return
fi
fi
fi
error_and_exit "$(cat /tmp/dd_stderr)"
fi
}
2024-10-18 23:58:03 +08:00
get_dick_sector_count() {
# cat /proc/partitions
2024-10-18 23:58:03 +08:00
blockdev --getsz "$1"
2023-07-25 00:21:08 +08:00
}
2023-05-03 22:22:21 +08:00
2024-10-18 23:58:03 +08:00
get_disk_size() {
blockdev --getsize64 "$1"
}
2023-07-25 00:21:08 +08:00
is_xda_gt_2t() {
2024-10-18 23:58:03 +08:00
disk_size=$(get_disk_size /dev/$xda)
2023-07-25 00:21:08 +08:00
disk_2t=$((2 * 1024 * 1024 * 1024 * 1024))
[ "$disk_size" -gt "$disk_2t" ]
}
2023-05-13 00:14:46 +08:00
2023-07-25 00:21:08 +08:00
create_part() {
# 除了 dd 都会用到
2024-09-06 23:00:36 +08:00
info "Create Part"
2023-05-03 22:22:21 +08:00
# 分区工具
apk add parted e2fsprogs
if is_efi; then
apk add dosfstools
fi
2023-07-28 21:27:16 +08:00
# 清除分区签名
2023-10-22 19:07:12 +08:00
# TODO: 先检测iso链接/各种链接
# wipefs -a /dev/$xda
2023-05-03 22:22:21 +08:00
2023-07-25 00:21:08 +08:00
# xda*1 星号用于 nvme0n1p1 的字母 p
2023-09-16 20:00:05 +08:00
# shellcheck disable=SC2154
2023-07-25 00:21:08 +08:00
if [ "$distro" = windows ]; then
2024-09-06 23:00:33 +08:00
if ! size_bytes=$(get_http_file_size "$iso"); then
# 默认值最大的iso 23h2 假设 7g
size_bytes=$((7 * 1024 * 1024 * 1024))
fi
# 按iso容量计算分区大小
2024-11-01 21:17:25 +08:00
# 200m 用于驱动/文件系统自身占用 + pagefile
# 理论上 installer 分区可以删除 boot.wim这样就不用额外添加 200m但是
# 1. vista/2008 不能删除 boot.wim
# 2. 下载镜像前不知道是 vista/2008因为 --image-name 可以随便输入
# 因此还是要额外添加 200m
part_size="$((size_bytes / 1024 / 1024 + 200))MiB"
2024-01-22 19:00:17 +08:00
apk add ntfs-3g-progs
2023-07-25 00:21:08 +08:00
# 虽然ntfs3不需要fuse但wimmount需要所以还是要保留
modprobe fuse ntfs3
if is_efi; then
# efi
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' fat32 1MiB 1025MiB \
mkpart '" "' fat32 1025MiB 1041MiB \
2024-11-01 21:17:25 +08:00
mkpart '" "' ntfs 1041MiB -${part_size} \
2023-10-22 19:07:12 +08:00
mkpart '" "' ntfs -${part_size} 100% \
2023-07-25 00:21:08 +08:00
set 1 boot on \
set 2 msftres on \
set 3 msftdata on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
mkfs.fat -n efi /dev/$xda*1 #1 efi
dd if=/dev/zero of="$(ls /dev/$xda*2)" bs=1M count=16 #2 msr
2024-11-01 21:17:25 +08:00
mkfs.ntfs -f -F -L os /dev/$xda*3 #3 os
2024-10-01 20:05:18 +08:00
mkfs.ntfs -f -F -L installer /dev/$xda*4 #4 installer
2023-07-25 00:21:08 +08:00
else
# bios + mbr 启动盘最大可用 2t
is_xda_gt_2t && max_usable_size=2TiB || max_usable_size=100%
2023-07-25 00:21:08 +08:00
parted /dev/$xda -s -- \
mklabel msdos \
2023-10-22 19:07:12 +08:00
mkpart primary ntfs 1MiB -${part_size} \
mkpart primary ntfs -${part_size} ${max_usable_size} \
2023-07-25 00:21:08 +08:00
set 1 boot on
update_part
2023-09-16 20:00:05 +08:00
2024-11-01 21:17:25 +08:00
mkfs.ntfs -f -F -L os /dev/$xda*1 #1 os
2024-10-01 20:05:18 +08:00
mkfs.ntfs -f -F -L installer /dev/$xda*2 #2 installer
2023-07-25 00:21:08 +08:00
fi
elif is_use_cloud_image; then
2024-09-06 23:00:33 +08:00
installer_part_size="$(get_cloud_image_part_size)"
2024-04-27 09:45:10 +08:00
# 这几个系统不使用dd而是复制文件
2024-11-26 22:09:05 +08:00
if [ "$distro" = centos ] || [ "$distro" = almalinux ] || [ "$distro" = rocky ] ||
2024-07-07 17:07:21 +08:00
[ "$distro" = oracle ] || [ "$distro" = redhat ] ||
[ "$distro" = anolis ] || [ "$distro" = opencloudos ] || [ "$distro" = openeuler ] ||
2024-04-27 09:45:10 +08:00
[ "$distro" = ubuntu ]; then
# 这里的 fs 没有用,最终使用目标系统的格式化工具
fs=ext4
2023-09-16 20:00:05 +08:00
if is_efi; then
parted /dev/$xda -s -- \
mklabel gpt \
2024-04-27 09:45:10 +08:00
mkpart '" "' fat32 1MiB 101MiB \
mkpart '" "' $fs 101MiB -$installer_part_size \
mkpart '" "' ext4 -$installer_part_size 100% \
2023-09-16 20:00:05 +08:00
set 1 esp on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
mkfs.fat -n efi /dev/$xda*1 #1 efi
echo #2 os 用目标系统的格式化工具
mkfs.ext4 -F -L installer /dev/$xda*3 #3 installer
2023-09-16 20:00:05 +08:00
else
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' ext4 1MiB 2MiB \
2024-04-27 09:45:10 +08:00
mkpart '" "' $fs 2MiB -$installer_part_size \
mkpart '" "' ext4 -$installer_part_size 100% \
2023-09-16 20:00:05 +08:00
set 1 bios_grub on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
echo #1 bios_boot
echo #2 os 用目标系统的格式化工具
mkfs.ext4 -F -L installer /dev/$xda*3 #3 installer
2023-09-16 20:00:05 +08:00
fi
2023-09-16 20:00:07 +08:00
else
2024-04-27 09:45:10 +08:00
# 使用 dd qcow2
# fedora debian opensuse arch gentoo
2023-09-16 20:00:07 +08:00
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' ext4 1MiB -$installer_part_size \
mkpart '" "' ext4 -$installer_part_size 100%
update_part
2023-09-16 20:00:07 +08:00
2024-10-01 20:05:18 +08:00
mkfs.ext4 -F -L os /dev/$xda*1 #1 os
mkfs.ext4 -F -L installer /dev/$xda*2 #2 installer
2023-09-16 20:00:05 +08:00
fi
2024-08-19 00:42:12 +08:00
elif [ "$distro" = alpine ] || [ "$distro" = arch ] || [ "$distro" = gentoo ] || [ "$distro" = nixos ]; then
2024-09-09 22:56:13 +08:00
# alpine 本身关闭了 64bit ext4
# https://gitlab.alpinelinux.org/alpine/alpine-conf/-/blob/3.18.1/setup-disk.in?ref_type=tags#L908
# 而且 alpine 的 extlinux 不兼容 64bit ext4
[ "$distro" = alpine ] && ext4_opts="-O ^64bit" || ext4_opts=
2023-12-02 23:30:39 +08:00
if is_efi; then
# efi
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' fat32 1MiB 101MiB \
mkpart '" "' ext4 101MiB 100% \
set 1 boot on
update_part
2023-12-02 23:30:39 +08:00
2024-10-01 20:05:18 +08:00
mkfs.fat /dev/$xda*1 #1 efi
mkfs.ext4 -F $ext4_opts /dev/$xda*2 #2 os
2023-12-02 23:30:39 +08:00
elif is_xda_gt_2t; then
# bios > 2t
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' ext4 1MiB 2MiB \
mkpart '" "' ext4 2MiB 100% \
set 1 bios_grub on
update_part
2023-12-02 23:30:39 +08:00
2024-10-01 20:05:18 +08:00
echo #1 bios_boot
mkfs.ext4 -F $ext4_opts /dev/$xda*2 #2 os
2023-12-02 23:30:39 +08:00
else
# bios
parted /dev/$xda -s -- \
mklabel msdos \
mkpart primary ext4 1MiB 100% \
set 1 boot on
update_part
2023-12-02 23:30:39 +08:00
2024-10-01 20:05:18 +08:00
mkfs.ext4 -F $ext4_opts /dev/$xda*1 #1 os
2023-12-02 23:30:39 +08:00
fi
2023-07-25 00:21:08 +08:00
else
# 安装红帽系或ubuntu
2023-07-25 00:21:08 +08:00
# 对于红帽系是临时分区表,安装时除了 installer 分区,其他分区会重建为默认的大小
# 对于ubuntu是最终分区表因为 ubuntu 的安装器不能调整个别分区,只能重建整个分区表
# installer 2g分区用fat格式刚好塞得下ubuntu-22.04.3 iso而ext4塞不下或者需要改参数
2024-09-06 23:00:33 +08:00
if [ "$distro" = ubuntu ]; then
if ! size_bytes=$(get_http_file_size "$iso"); then
# 默认值,假设 iso 3g
size_bytes=$((3 * 1024 * 1024 * 1024))
fi
installer_part_size="$(get_part_size_mb_for_file_size_b $size_bytes)MiB"
2024-09-06 23:00:33 +08:00
else
# redhat
installer_part_size=2GiB
fi
# centos 7 无法加载alpine格式化的ext4
# 要关闭这个属性
2024-09-09 22:56:13 +08:00
ext4_opts="-O ^metadata_csum"
apk add dosfstools
2024-09-06 23:00:33 +08:00
2023-07-25 00:21:08 +08:00
if is_efi; then
# efi
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' fat32 1MiB 1025MiB \
mkpart '" "' ext4 1025MiB -$installer_part_size \
mkpart '" "' ext4 -$installer_part_size 100% \
2023-07-25 00:21:08 +08:00
set 1 boot on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
mkfs.fat -n efi /dev/$xda*1 #1 efi
mkfs.ext4 -F -L os /dev/$xda*2 #2 os
mkfs.ext4 -F -L installer $ext4_opts /dev/$xda*3 #2 installer
2023-07-25 00:21:08 +08:00
elif is_xda_gt_2t; then
# bios > 2t
parted /dev/$xda -s -- \
mklabel gpt \
mkpart '" "' ext4 1MiB 2MiB \
mkpart '" "' ext4 2MiB -$installer_part_size \
mkpart '" "' ext4 -$installer_part_size 100% \
2023-07-25 00:21:08 +08:00
set 1 bios_grub on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
echo #1 bios_boot
mkfs.ext4 -F -L os /dev/$xda*2 #2 os
mkfs.ext4 -F -L installer $ext4_opts /dev/$xda*3 #3 installer
2023-07-25 00:21:08 +08:00
else
# bios
parted /dev/$xda -s -- \
mklabel msdos \
mkpart primary ext4 1MiB -$installer_part_size \
mkpart primary ext4 -$installer_part_size 100% \
2023-07-25 00:21:08 +08:00
set 1 boot on
update_part
2023-09-16 20:00:05 +08:00
2024-10-01 20:05:18 +08:00
mkfs.ext4 -F -L os /dev/$xda*1 #1 os
mkfs.ext4 -F -L installer $ext4_opts /dev/$xda*2 #2 installer
2023-07-25 00:21:08 +08:00
fi
update_part
2023-05-25 20:15:12 +08:00
fi
update_part
2023-12-02 23:30:39 +08:00
# alpine 删除分区工具,防止 256M 小机爆内存
# setup-disk /dev/sda 会保留格式化工具,我们也保留
if [ "$distro" = alpine ]; then
apk del parted
fi
2023-07-25 00:21:08 +08:00
}
2023-05-03 22:22:21 +08:00
2023-07-28 21:27:16 +08:00
mount_pseudo_fs() {
os_dir=$1
# https://wiki.archlinux.org/title/Chroot#Using_chroot
2023-09-16 20:00:05 +08:00
mount -t proc /proc $os_dir/proc/
mount -t sysfs /sys $os_dir/sys/
mount --rbind /dev $os_dir/dev/
mount --rbind /run $os_dir/run/
2023-07-28 21:27:16 +08:00
if is_efi; then
2023-09-16 20:00:05 +08:00
mount --rbind /sys/firmware/efi/efivars $os_dir/sys/firmware/efi/efivars/
2023-07-28 21:27:16 +08:00
fi
}
2024-05-22 21:53:32 +08:00
get_yq_name() {
if grep -q '3\.1[6789]' /etc/alpine-release; then
echo yq
else
echo yq-go
fi
}
create_cloud_init_network_config() {
ci_file=$1
recognize_static6=${2:-true}
recognize_ipv6_types=${3:-true}
2024-10-21 00:30:02 +08:00
info "Create Cloud Init network config"
2024-10-21 00:30:02 +08:00
# 防止文件未创建
mkdir -p "$(dirname "$ci_file")"
touch "$ci_file"
2024-05-22 21:53:32 +08:00
apk add "$(get_yq_name)"
need_set_dns4=false
need_set_dns6=false
config_id=0
for ethx in $(get_eths); do
get_netconf_to mac_addr
# shellcheck disable=SC2154
yq -i ".network.version=1 |
.network.config[$config_id].type=\"physical\" |
.network.config[$config_id].name=\"$ethx\" |
.network.config[$config_id].mac_address=\"$mac_addr\"
" $ci_file
subnet_id=0
# ipv4
if is_dhcpv4; then
yq -i ".network.config[$config_id].subnets[$subnet_id] = {\"type\": \"dhcp4\"}" $ci_file
subnet_id=$((subnet_id + 1))
elif is_staticv4; then
need_set_dns4=true
get_netconf_to ipv4_addr
get_netconf_to ipv4_gateway
yq -i ".network.config[$config_id].subnets[$subnet_id] = {
\"type\": \"static\",
\"address\": \"$ipv4_addr\",
\"gateway\": \"$ipv4_gateway\" }
" $ci_file
# 旧版 cloud-init 有 bug
# 有的版本会只从第一种配置中读取 dns有的从第二种读取
# 因此写两种配置
2024-11-29 22:13:53 +08:00
# https://github.com/canonical/cloud-init/commit/1b8030e0c7fd6fbff7e38ad1e3e6266ae50c83a5
2024-08-19 00:33:59 +08:00
for cur in $(get_current_dns 4); do
yq -i ".network.config[$config_id].subnets[$subnet_id].dns_nameservers += [\"$cur\"]" $ci_file
done
subnet_id=$((subnet_id + 1))
fi
# ipv6
# slaac: ipv6_slaac
# └─enable_other_flag: ipv6_dhcpv6-stateless
# dhcpv6: ipv6_dhcpv6-stateful
# ipv6
if is_slaac; then
if $recognize_ipv6_types; then
if is_enable_other_flag; then
type=ipv6_dhcpv6-stateless
else
type=ipv6_slaac
fi
else
type=dhcp6
fi
yq -i ".network.config[$config_id].subnets[$subnet_id] = {\"type\": \"$type\"}" $ci_file
elif is_dhcpv6; then
if $recognize_ipv6_types; then
type=ipv6_dhcpv6-stateful
else
type=dhcp6
fi
yq -i ".network.config[$config_id].subnets[$subnet_id] = {\"type\": \"$type\"}" $ci_file
elif is_staticv6; then
get_netconf_to ipv6_addr
get_netconf_to ipv6_gateway
if $recognize_static6; then
type_ipv6_static=static6
else
type_ipv6_static=static
fi
yq -i ".network.config[$config_id].subnets[$subnet_id] = {
\"type\": \"$type_ipv6_static\",
\"address\": \"$ipv6_addr\",
\"gateway\": \"$ipv6_gateway\" }
" $ci_file
if should_disable_ra_slaac; then
yq -i ".network.config[$config_id].accept-ra = false" $ci_file
fi
fi
# 有 ipv6 但需设置 dns 的情况
2024-08-19 00:33:59 +08:00
if is_need_manual_set_dnsv6; then
need_set_dns6=true
2024-08-19 00:33:59 +08:00
for cur in $(get_current_dns 6); do
yq -i ".network.config[$config_id].subnets[$subnet_id].dns_nameservers += [\"$cur\"]" $ci_file
done
fi
config_id=$((config_id + 1))
done
if $need_set_dns4 || $need_set_dns6; then
yq -i ".network.config[$config_id].type=\"nameserver\"" $ci_file
2024-08-19 00:33:59 +08:00
if $need_set_dns4; then
for cur in $(get_current_dns 4); do
yq -i ".network.config[$config_id].address += [\"$cur\"]" $ci_file
done
fi
2024-08-19 00:33:59 +08:00
if $need_set_dns6; then
for cur in $(get_current_dns 6); do
yq -i ".network.config[$config_id].address += [\"$cur\"]" $ci_file
done
fi
# 如果 network.config[$config_id] 没有 address则删除避免低版本 cloud-init 报错
yq -i "del(.network.config[$config_id] | select(has(\"address\") | not))" $ci_file
fi
2024-04-28 00:02:31 +08:00
2024-05-22 21:53:32 +08:00
apk del "$(get_yq_name)"
}
2024-10-21 00:30:02 +08:00
# 实测没用,生成的 machine-id 是固定的
# 而且 lightsail centos 9 模板 machine-id 也是相同的,显然相同 id 不是个问题
2024-10-21 00:30:02 +08:00
clear_machine_id() {
os_dir=$1
2024-10-21 00:30:02 +08:00
# https://www.freedesktop.org/software/systemd/man/latest/machine-id.html
# gentoo 不会自动创建该文件
echo uninitialized >$os_dir/etc/machine-id
2024-10-21 00:30:02 +08:00
# https://build.opensuse.org/projects/Virtualization:Appliances:Images:openSUSE-Leap-15.5/packages/kiwi-templates-Minimal/files/config.sh?expand=1
rm -f $os_dir/var/lib/systemd/random-seed
}
2023-07-28 21:27:16 +08:00
download_cloud_init_config() {
os_dir=$1
recognize_static6=$2
recognize_ipv6_types=$3
2023-07-28 21:27:16 +08:00
ci_file=$os_dir/etc/cloud/cloud.cfg.d/99_fallback.cfg
download $confhome/cloud-init.yaml $ci_file
# 删除注释行,除了第一行
sed -i '1!{/^[[:space:]]*#/d}' $ci_file
2023-07-28 21:27:16 +08:00
2024-05-22 21:56:11 +08:00
# 修改密码
2024-10-13 22:58:12 +08:00
# 不能用 sed 替换,因为含有特殊字符
content=$(cat $ci_file)
echo "${content//@PASSWORD@/$(get_password_linux_sha512)}" >$ci_file
2024-05-22 21:56:11 +08:00
2024-10-12 23:07:01 +08:00
# 修改 ssh 端口
if is_need_change_ssh_port; then
sed -i "s/@SSH_PORT@/$ssh_port/g" $ci_file
else
sed -i "/@SSH_PORT@/d" $ci_file
fi
2023-07-28 21:27:16 +08:00
# swapfile
2023-09-21 00:12:35 +08:00
# 如果分区表中已经有swapfile就跳过例如arch
if ! grep -w swap $os_dir/etc/fstab; then
2023-07-28 21:27:16 +08:00
# btrfs
# 目前只有 arch 和 fedora 镜像使用 btrfs
2024-05-22 22:32:43 +08:00
# 等 fedora 39 cloud-init 升级到 v23.3 后删除
2023-07-28 21:27:16 +08:00
if mount | grep 'on /os type btrfs'; then
2023-09-16 20:00:05 +08:00
insert_into_file $ci_file after '^runcmd:' <<EOF
2023-07-28 21:27:16 +08:00
- btrfs filesystem mkswapfile --size 1G /swapfile
- swapon /swapfile
- echo "/swapfile none swap defaults 0 0" >> /etc/fstab
- systemctl daemon-reload
2023-07-28 21:27:16 +08:00
EOF
else
# ext4 xfs
cat <<EOF >>$ci_file
swap:
filename: /swapfile
size: auto
EOF
fi
fi
create_cloud_init_network_config "$ci_file" "$recognize_static6" "$recognize_ipv6_types"
2023-07-28 21:27:16 +08:00
}
modify_windows() {
os_dir=$1
2024-09-06 23:00:36 +08:00
info "Modify Windows"
# https://learn.microsoft.com/windows-hardware/manufacture/desktop/windows-setup-states
# https://learn.microsoft.com/troubleshoot/azure/virtual-machines/reset-local-password-without-agent
# https://learn.microsoft.com/windows-hardware/manufacture/desktop/add-a-custom-script-to-windows-setup
# 判断用 SetupComplete 还是组策略
state_ini=$os_dir/Windows/Setup/State/State.ini
cat $state_ini
if grep -q IMAGE_STATE_COMPLETE $state_ini; then
use_gpo=true
else
use_gpo=false
fi
2024-10-12 23:07:01 +08:00
# bat 列表
bats=
# 1. rdp 端口
if is_need_change_rdp_port; then
create_win_change_rdp_port_script $os_dir/windows-change-rdp-port.bat "$rdp_port"
bats="$bats windows-change-rdp-port.bat"
fi
# 2. 允许 ping
if is_allow_ping; then
download $confhome/windows-allow-ping.bat $os_dir/windows-allow-ping.bat
bats="$bats windows-allow-ping.bat"
fi
# 3. 合并分区
# 可能 unattend.xml 已经设置了ExtendOSPartition不过运行resize没副作用
download $confhome/windows-resize.bat $os_dir/windows-resize.bat
2024-10-12 23:07:01 +08:00
bats="$bats windows-resize.bat"
# 4. 网络设置
for ethx in $(get_eths); do
create_win_set_netconf_script $os_dir/windows-set-netconf-$ethx.bat
bats="$bats windows-set-netconf-$ethx.bat"
done
if $use_gpo; then
# 使用组策略
gpt_ini=$os_dir/Windows/System32/GroupPolicy/gpt.ini
scripts_ini=$os_dir/Windows/System32/GroupPolicy/Machine/Scripts/scripts.ini
mkdir -p "$(dirname $scripts_ini)"
# 备份 ini
for file in $gpt_ini $scripts_ini; do
if [ -f $file ]; then
cp $file $file.orig
fi
done
# gpt.ini
cat >$gpt_ini <<EOF
[General]
gPCFunctionalityVersion=2
gPCMachineExtensionNames=[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}]
Version=1
EOF
unix2dos $gpt_ini
# scripts.ini
if ! [ -e $scripts_ini ]; then
touch $scripts_ini
fi
if ! grep -F '[Startup]' $scripts_ini; then
echo '[Startup]' >>$scripts_ini
fi
# 注意没用 pipefail 的话,错误码取自最后一个管道
if num=$(grep -Eo '^[0-9]+' $scripts_ini | sort -n | tail -1 | grep .); then
num=$((num + 1))
else
num=0
fi
bats="$bats windows-del-gpo.bat"
for bat in $bats; do
echo "${num}CmdLine=%SystemDrive%\\$bat" >>$scripts_ini
echo "${num}Parameters=" >>$scripts_ini
num=$((num + 1))
done
cat $scripts_ini
unix2dos $scripts_ini
# windows-del-gpo.bat
download $confhome/windows-del-gpo.bat $os_dir/windows-del-gpo.bat
else
# 使用 SetupComplete
setup_complete=$os_dir/Windows/Setup/Scripts/SetupComplete.cmd
mkdir -p "$(dirname $setup_complete)"
# 添加到 C:\Setup\Scripts\SetupComplete.cmd 最前面
# call 防止子 bat 删除自身后中断主脚本
2023-12-14 00:11:32 +08:00
setup_complete_mod=$(mktemp)
for bat in $bats; do
2023-12-14 00:11:32 +08:00
echo "if exist %SystemDrive%\\$bat (call %SystemDrive%\\$bat)" >>$setup_complete_mod
done
2023-12-14 00:11:32 +08:00
# 复制原来的内容
if [ -f $setup_complete ]; then
2023-12-14 00:11:32 +08:00
cat $setup_complete >>$setup_complete_mod
fi
2023-12-14 00:11:32 +08:00
unix2dos $setup_complete_mod
# cat 可以保留权限
cat $setup_complete_mod >$setup_complete
fi
}
get_axx64() {
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
esac
}
2024-05-22 22:32:43 +08:00
is_file_or_link() {
# -e / -f 坏软连接,返回 false
# -L 坏软连接,返回 true
2024-05-22 22:32:43 +08:00
[ -f $1 ] || [ -L $1 ]
}
cp_resolv_conf() {
os_dir=$1
if is_file_or_link $os_dir/etc/resolv.conf &&
! is_file_or_link $os_dir/etc/resolv.conf.orig; then
mv $os_dir/etc/resolv.conf $os_dir/etc/resolv.conf.orig
fi
cp -f /etc/resolv.conf $os_dir/etc/resolv.conf
}
rm_resolv_conf() {
os_dir=$1
rm -f $os_dir/etc/resolv.conf $os_dir/etc/resolv.conf.orig
}
restore_resolv_conf() {
os_dir=$1
2024-05-22 22:32:43 +08:00
if is_file_or_link $os_dir/etc/resolv.conf.orig; then
mv -f $os_dir/etc/resolv.conf.orig $os_dir/etc/resolv.conf
fi
}
2024-12-06 22:50:05 +08:00
# 抄 https://github.com/alpinelinux/alpine-conf/blob/3.18.1/setup-disk.in#L421
get_alpine_firmware_pkgs() {
# 需要有 modloop不然 modinfo 会报错
ensure_service_started modloop >&2
# 如果不在单独的文件夹,则用 linux-firmware-other
# 如果在单独的文件夹,则用 linux-firmware-xxx
# 如果不需要 firmware则用 linux-firmware-none
firmware_pkgs=$(
cd /sys/module && modinfo -F firmware -- * 2>/dev/null |
awk -F/ '{print $1 == $0 ? "linux-firmware-other" : "linux-firmware-"$1}' |
sort -u
)
# 使用 command 因为自己覆盖了 apk 添加了 >&2
retry 5 command apk search --quiet --exact ${firmware_pkgs:-linux-firmware-none}
}
get_ucode_firmware_pkgs() {
2024-12-06 22:50:05 +08:00
is_virt && return
case "$distro" in
2024-11-26 22:09:05 +08:00
centos | almalinux | rocky | oracle | redhat | anolis | opencloudos | openeuler) os=elol ;;
*) os=$distro ;;
esac
case "$os-$(get_cpu_vendor)" in
2024-12-06 22:50:05 +08:00
# alpine 的 linux-firmware 以文件夹进行拆分
# setup-alpine 会自动安装需要的 firmwaremodloop 没挂载则无效)
# https://github.com/alpinelinux/alpine-conf/blob/3.18.1/setup-disk.in#L421
alpine-intel) echo intel-ucode ;;
alpine-amd) echo amd-ucode ;;
alpine-*) ;;
debian-intel) echo firmware-linux intel-microcode ;;
debian-amd) echo firmware-linux amd64-microcode ;;
debian-*) echo firmware-linux ;;
ubuntu-intel) echo linux-firmware intel-microcode ;;
ubuntu-amd) echo linux-firmware amd64-microcode ;;
ubuntu-*) echo linux-firmware ;;
# 无法同时安装 kernel-firmware kernel-firmware-intel
opensuse-intel) echo kernel-firmware ucode-intel ;;
opensuse-amd) echo kernel-firmware ucode-amd ;;
opensuse-*) echo kernel-firmware ;;
arch-intel) echo linux-firmware intel-ucode ;;
arch-amd) echo linux-firmware amd-ucode ;;
arch-*) echo linux-firmware ;;
gentoo-intel) echo linux-firmware intel-microcode ;;
gentoo-amd) echo linux-firmware ;;
gentoo-*) echo linux-firmware ;;
nixos-intel) echo linux-firmware microcodeIntel ;;
nixos-amd) echo linux-firmware microcodeAmd ;;
nixos-*) echo linux-firmware ;;
fedora-intel) echo linux-firmware microcode_ctl ;;
fedora-amd) echo linux-firmware amd-ucode-firmware microcode_ctl ;;
fedora-*) echo linux-firmware microcode_ctl ;;
elol-intel) echo linux-firmware microcode_ctl ;;
elol-amd) echo linux-firmware microcode_ctl ;;
elol-*) echo linux-firmware microcode_ctl ;;
esac
}
modify_linux() {
os_dir=$1
2024-09-06 23:00:36 +08:00
info "Modify Linux"
find_and_mount() {
mount_point=$1
mount_dev=$(awk "\$2==\"$mount_point\" {print \$1}" $os_dir/etc/fstab)
if [ -n "$mount_dev" ]; then
mount $mount_dev $os_dir$mount_point
fi
}
# 修复 onlink 网关
add_onlink_script_if_need() {
for ethx in $(get_eths); do
if is_staticv4 || is_staticv6; then
fix_sh=cloud-init-fix-onlink.sh
download "$confhome/$fix_sh" "$os_dir/$fix_sh"
insert_into_file "$ci_file" after '^runcmd:' <<EOF
- bash "/$fix_sh" && rm -f "/$fix_sh"
EOF
break
fi
done
}
2023-08-03 22:38:38 +08:00
download_cloud_init_config $os_dir
2023-09-10 22:23:04 +08:00
clear_machine_id $os_dir
# el/ol/fedora/国产fork
# 1. 禁用 selinux kdump
# 2. 添加微码+固件
2023-08-03 22:38:38 +08:00
if [ -f $os_dir/etc/redhat-release ]; then
find_and_mount /boot
find_and_mount /boot/efi
mount_pseudo_fs $os_dir
cp_resolv_conf $os_dir
2023-08-03 22:38:38 +08:00
disable_selinux_kdump $os_dir
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
is_have_cmd_on_disk $os_dir dnf && mgr=dnf || mgr=yum
2024-12-06 22:50:05 +08:00
chroot $os_dir $mgr install -y $fw_pkgs
fi
restore_resolv_conf $os_dir
2023-09-16 20:00:05 +08:00
fi
# debian
# 1. EOL 换源
# 2. 修复网络问题
# 3. 添加微码+固件
2024-04-27 09:45:10 +08:00
# 注意 ubuntu 也有 /etc/debian_version
2024-05-08 22:17:33 +08:00
if [ "$distro" = debian ]; then
# 修复 onlink 网关
add_onlink_script_if_need
mount_pseudo_fs $os_dir
cp_resolv_conf $os_dir
find_and_mount /boot
find_and_mount /boot/efi
# 获取当前开启的 Components, 后面要用
if [ -f $os_dir/etc/apt/sources.list.d/debian.sources ]; then
comps=$(grep ^Components: $os_dir/etc/apt/sources.list.d/debian.sources | head -1 | cut -d' ' -f2-)
else
comps=$(grep '^deb ' $os_dir/etc/apt/sources.list | head -1 | cut -d' ' -f4-)
fi
# EOL 处理
if is_elts; then
wget https://deb.freexian.com/extended-lts/archive-key.gpg \
-O $os_dir/etc/apt/trusted.gpg.d/freexian-archive-extended-lts.gpg
is_in_china &&
mirror=http://mirror.nju.edu.cn/debian-elts ||
mirror=http://deb.freexian.com/extended-lts
codename=$(grep '^VERSION_CODENAME=' $os_dir/etc/os-release | cut -d= -f2)
if [ -f $os_dir/etc/apt/sources.list.d/debian.sources ]; then
cat <<EOF >$os_dir/etc/apt/sources.list.d/debian.sources
Types: deb
URIs: $mirror
Suites: $codename
Components: $comps
Signed-By: /etc/apt/trusted.gpg.d/freexian-archive-extended-lts.gpg
EOF
else
echo "deb $mirror $codename $comps" >$os_dir/etc/apt/sources.list
fi
fi
# 检测机器是否能用 cloud 内核
# shellcheck disable=SC2046
if ls $os_dir/boot/vmlinuz-*-cloud-$(get_axx64) 2>/dev/null &&
! sh /can_use_cloud_kernel.sh "$xda" $(get_eths); then
chroot_apt_install $os_dir "linux-image-$(get_axx64)"
# 标记云内核包
# apt-mark showmanual 结果为空,返回值也是 0
if pkgs=$(chroot $os_dir apt-mark showmanual "linux-*-cloud-$(get_axx64)" | grep .); then
chroot $os_dir apt-mark auto $pkgs
# 使用 autoremove
chroot_apt_autoremove $os_dir
fi
fi
# 微码+固件
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
# debian 10 11 的 iucode-tool 在 contrib 里面
# debian 12 的 iucode-tool 在 main 里面
[ "$releasever" -ge 12 ] &&
comps_to_add=non-free-firmware ||
comps_to_add="contrib non-free"
if [ -f $os_dir/etc/apt/sources.list.d/debian.sources ]; then
file=$os_dir/etc/apt/sources.list.d/debian.sources
search='^[# ]*Components:'
else
file=$os_dir/etc/apt/sources.list
search='^[# ]*deb'
fi
for c in $comps_to_add; do
if ! echo "$comps" | grep -wq "$c"; then
sed -Ei "/$search/s/$/ $c/" $file
fi
done
2024-12-06 22:50:05 +08:00
chroot_apt_install $os_dir $fw_pkgs
fi
2024-07-14 22:11:53 +08:00
if [ "$releasever" -le 11 ]; then
2024-09-06 23:00:35 +08:00
chroot $os_dir apt-get update
if true; then
# 将 debian 11 设置为 12 一样的网络管理器
# 可解决 ifupdown dhcp 不支持 24位掩码+不规则网关的问题
chroot_apt_install $os_dir netplan.io
chroot $os_dir systemctl disable networking resolvconf
chroot $os_dir systemctl enable systemd-networkd systemd-resolved
rm_resolv_conf $os_dir
ln -sf ../run/systemd/resolve/stub-resolv.conf $os_dir/etc/resolv.conf
insert_into_file $os_dir/etc/cloud/cloud.cfg.d/99_fallback.cfg after '#cloud-config' <<EOF
system_info:
network:
renderers: [netplan]
activators: [netplan]
EOF
else
# debian 11 默认不支持 rdnss要安装 rdnssd 或者 nm
chroot_apt_install $os_dir rdnssd
fi
fi
# 不会自动建立链接,因此不能删除
restore_resolv_conf $os_dir
2023-08-03 22:38:38 +08:00
fi
2023-09-16 20:00:07 +08:00
# opensuse
# 1. kernel-default-base 缺少 nvme 驱动,换成 kernel-default
# 2. 添加微码+固件
# https://documentation.suse.com/smart/virtualization-cloud/html/minimal-vm/index.html
if grep -q opensuse $os_dir/etc/os-release; then
create_swap_if_ram_less_than 1024 $os_dir/swapfile
mount_pseudo_fs $os_dir
cp_resolv_conf $os_dir
find_and_mount /boot
find_and_mount /boot/efi
# opensuse leap
if grep opensuse-leap $os_dir/etc/os-release; then
# 修复 onlink 网关
add_onlink_script_if_need
fi
# opensuse tumbleweed
# 更新到 cloud-init 24.1 后删除
if grep opensuse-tumbleweed $os_dir/etc/os-release; then
touch $os_dir/etc/NetworkManager/NetworkManager.conf
fi
# 不能同时装 kernel-default-base 和 kernel-default
chroot $os_dir zypper remove -y kernel-default-base
# 固件+微码
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot $os_dir zypper install -y $fw_pkgs
fi
# 选择新内核
# 只有 leap 有 kernel-azure
if grep -q opensuse-leap $os_dir/etc/os-release && [ "$(get_cloud_vendor)" = azure ]; then
kernel='kernel-azure'
else
kernel='kernel-default'
fi
# 必须设置一个密码,否则报错
# Failed to get root password hash
# Failed to import /etc/uefi/certs/76B6A6A0.crt
# warning: %post(kernel-default-5.14.21-150500.55.83.1.x86_64) scriptlet failed, exit status 255
echo "root:$(mkpasswd '')" | chroot $os_dir chpasswd -e
chroot $os_dir zypper install -y $kernel
chroot $os_dir passwd -d root
restore_resolv_conf $os_dir
swapoff $os_dir/swapfile
rm -f $os_dir/swapfile
fi
2023-12-22 23:52:47 +08:00
# arch
if [ -f $os_dir/etc/arch-release ]; then
# 修复 onlink 网关
add_onlink_script_if_need
# 同步证书
2024-05-22 22:32:43 +08:00
cp_resolv_conf $os_dir
2023-12-22 23:52:47 +08:00
mount_pseudo_fs $os_dir
chroot $os_dir pacman-key --init
chroot $os_dir pacman-key --populate
2024-05-22 22:32:43 +08:00
rm_resolv_conf $os_dir
2023-12-22 23:52:47 +08:00
fi
2023-09-16 20:00:07 +08:00
# gentoo
if [ -f $os_dir/etc/gentoo-release ]; then
# 挂载伪文件系统
mount_pseudo_fs $os_dir
2024-05-22 22:32:43 +08:00
cp_resolv_conf $os_dir
2023-09-16 20:00:07 +08:00
# 在这里修改密码而不是用cloud-init因为我们的默认密码太弱
2024-10-13 22:58:12 +08:00
is_password_plaintext && sed -i 's/enforce=everyone/enforce=none/' $os_dir/etc/security/passwdqc.conf
echo "root:$(get_password_linux_sha512)" | chroot $os_dir chpasswd -e
is_password_plaintext && sed -i 's/enforce=none/enforce=everyone/' $os_dir/etc/security/passwdqc.conf
2023-09-16 20:00:07 +08:00
# 下载仓库,选择 profile
2024-03-28 00:16:05 +08:00
chroot $os_dir emerge-webrsync
profile=$(chroot $os_dir eselect profile list | grep stable | grep systemd |
awk '{print length($2), $2}' | sort -n | head -1 | awk '{print $2}')
chroot $os_dir eselect profile set $profile
2023-09-16 20:00:07 +08:00
# 删除 resolv.conf不然 systemd-resolved 无法创建软链接
2024-05-22 22:32:43 +08:00
rm_resolv_conf $os_dir
2023-09-16 20:00:07 +08:00
# 启用网络服务
chroot $os_dir systemctl enable systemd-networkd
chroot $os_dir systemctl enable systemd-resolved
# systemd-networkd 有时不会运行
# https://bugs.gentoo.org/910404 补丁好像没用
# https://github.com/systemd/systemd/issues/27718#issuecomment-1564877478
# 临时的解决办法是运行 networkctl如果启用了systemd-networkd服务会运行服务
insert_into_file $os_dir/lib/systemd/system/systemd-logind.service after '\[Service\]' <<EOF
ExecStartPost=-networkctl
EOF
# 如果创建了 cloud-init.disabled重启后网络不受 networkd 管理
# 因为网卡名变回了 ens3 而不是 eth0
# 因此要删除 networkd 的网卡名匹配
insert_into_file $ci_file after '^runcmd:' <<EOF
- sed -i '/^Name=/d' /etc/systemd/network/10-cloud-init-eth*.network
2023-09-16 20:00:07 +08:00
EOF
# 修复 onlink 网关
add_onlink_script_if_need
2023-09-16 20:00:07 +08:00
fi
# 修复 cloud-init + sysconfig / NetworkManager 的各种网络问题
if [ -d "$os_dir/etc/sysconfig" ] || [ -d "$os_dir/etc/NetworkManager" ]; then
fix_sysconfig_NetworkManager $os_dir
fi
# 查看 cloud-init 最终配置
if [ -f "$ci_file" ]; then
cat -n "$ci_file"
fi
2023-08-03 22:38:38 +08:00
}
modify_os_on_disk() {
only_process=$1
2024-09-06 23:00:36 +08:00
info "Modify disk if is $only_process"
update_part
# dd linux 的时候不用修改硬盘内容
if [ "$distro" = "dd" ] && ! lsblk -f /dev/$xda | grep ntfs; then
return
fi
mkdir -p /os
# 按分区容量大到小,依次寻找系统分区
2024-05-08 22:17:33 +08:00
for part in $(lsblk /dev/$xda*[0-9] --sort SIZE -no NAME | tac); do
# btrfs挂载的是默认子卷如果没有默认子卷挂载的是根目录
# fedora 云镜像没有默认子卷且系统在root子卷中
if mount -o ro /dev/$part /os; then
if [ "$only_process" = linux ]; then
if etc_dir=$({ ls -d /os/etc/ || ls -d /os/*/etc/; } 2>/dev/null); then
os_dir=$(dirname $etc_dir)
# 重新挂载为读写
mount -o remount,rw /os
modify_linux $os_dir
return
fi
elif [ "$only_process" = windows ]; then
2024-01-27 23:17:50 +08:00
# find 不是很聪明
# find /mnt/c -iname windows -type d -maxdepth 1
# find: /mnt/c/pagefile.sys: Permission denied
# find: /mnt/c/swapfile.sys: Permission denied
# shellcheck disable=SC2010
if ls -d /os/*/ | grep -i '/windows/' 2>/dev/null; then
# 重新挂载为读写、忽略大小写
umount /os
apk add ntfs-3g
mount.lowntfs-3g /dev/$part /os -o ignore_case
modify_windows /os
return
fi
fi
2023-10-23 21:33:14 +08:00
umount /os
fi
done
error_and_exit "Can't find os partition."
2023-07-28 21:27:16 +08:00
}
2024-08-19 00:33:59 +08:00
get_need_swap_size() {
need_ram=$1
phy_ram=$(get_approximate_ram_size)
if [ $need_ram -gt $phy_ram ]; then
echo $((need_ram - phy_ram))
else
echo 0
fi
2024-08-19 00:33:59 +08:00
}
2023-12-02 23:30:39 +08:00
create_swap_if_ram_less_than() {
need_ram=$1
swapfile=$2
2024-08-19 00:33:59 +08:00
swapsize=$(get_need_swap_size $need_ram)
2023-12-02 23:30:39 +08:00
if [ $swapsize -gt 0 ]; then
create_swap $swapsize $swapfile
fi
}
2023-07-30 00:35:32 +08:00
create_swap() {
2023-12-02 23:30:39 +08:00
swapsize=$1
swapfile=$2
2023-07-30 00:35:32 +08:00
if ! grep $swapfile /proc/swaps; then
2023-12-02 23:30:39 +08:00
fallocate -l ${swapsize}M $swapfile
chmod 0600 $swapfile
mkswap $swapfile
swapon $swapfile
2023-07-30 00:35:32 +08:00
fi
}
2024-07-07 17:07:21 +08:00
# arch gentoo 常规安装用
2024-10-12 23:07:01 +08:00
change_ssh_conf() {
2024-03-28 00:16:05 +08:00
os_dir=$1
2024-10-12 23:07:01 +08:00
key=$2
value=$3
sub_conf=$4
2024-03-28 00:16:05 +08:00
# arch 没有 /etc/ssh/sshd_config.d/ 文件夹
2024-10-12 23:07:01 +08:00
# opensuse tumbleweed 有 /etc/ssh/sshd_config.d/ 文件夹,但没有 /etc/ssh/sshd_config有/usr/etc/ssh/sshd_config
if grep -q 'Include.*/etc/ssh/sshd_config.d' $os_dir/etc/ssh/sshd_config ||
grep -q '^Include.*/etc/ssh/sshd_config.d/' $os_dir/usr/etc/ssh/sshd_config; then
2024-03-28 00:16:05 +08:00
mkdir -p $os_dir/etc/ssh/sshd_config.d/
2024-10-12 23:07:01 +08:00
echo "$key $value" >"$os_dir/etc/ssh/sshd_config.d/$sub_conf"
2024-03-28 00:16:05 +08:00
else
2024-10-12 23:07:01 +08:00
# 如果 sshd_config 存在此 key则替换
# 否则追加
line="^#?$key .*"
if grep -x "$line" $os_dir/etc/ssh/sshd_config; then
sed -Ei "s/$line/$key $value/" $os_dir/etc/ssh/sshd_config
else
echo "$key $value" >>$os_dir/etc/ssh/sshd_config
2024-03-28 00:16:05 +08:00
fi
fi
}
2024-10-12 23:07:01 +08:00
# arch gentoo 常规安装用
allow_root_password_login() {
os_dir=$1
change_ssh_conf "$os_dir" PermitRootLogin yes 01-permitrootlogin.conf
}
# arch gentoo 常规安装用
change_ssh_port() {
os_dir=$1
ssh_port=$2
change_ssh_conf "$os_dir" Port "$ssh_port" 01-change-ssh-port.conf
}
change_root_password() {
os_dir=$1
info 'change root password'
2024-10-13 22:58:12 +08:00
if is_password_plaintext; then
pam_d=$os_dir/etc/pam.d
2024-10-13 22:58:12 +08:00
[ -f $pam_d/chpasswd ] && has_pamd_chpasswd=true || has_pamd_chpasswd=false
2024-10-13 22:58:12 +08:00
if $has_pamd_chpasswd; then
cp $pam_d/chpasswd $pam_d/chpasswd.orig
2024-10-13 22:58:12 +08:00
# cat /etc/pam.d/chpasswd
# @include common-password
2024-10-13 22:58:12 +08:00
# cat /etc/pam.d/chpasswd
# #%PAM-1.0
# auth include system-auth
# account include system-auth
# password substack system-auth
# -password optional pam_gnome_keyring.so use_authtok
# password substack postlogin
2024-10-13 22:58:12 +08:00
# 通过 /etc/pam.d/chpasswd 找到 /etc/pam.d/system-auth 或者 /etc/pam.d/system-auth
# 再找到有 password 和 pam_unix.so 的行,并删除 use_authtok写入 /etc/pam.d/chpasswd
files=$(grep -E '^(password|@include)' $pam_d/chpasswd | awk '{print $NF}' | sort -u)
for file in $files; do
if [ -f "$pam_d/$file" ] && line=$(grep ^password "$pam_d/$file" | grep -F pam_unix.so); then
echo "$line" | sed 's/use_authtok//' >$pam_d/chpasswd
break
fi
done
fi
2024-10-13 22:58:12 +08:00
# 分两行写,不然遇到错误不会终止
plaintext=$(get_password_plaintext)
echo "root:$plaintext" | chroot $os_dir chpasswd
2024-10-13 22:58:12 +08:00
if $has_pamd_chpasswd; then
mv $pam_d/chpasswd.orig $pam_d/chpasswd
fi
else
echo "root:$(get_password_linux_sha512)" | chroot $os_dir chpasswd -e
fi
}
disable_selinux_kdump() {
os_dir=$1
# selinux
2024-11-29 22:13:52 +08:00
# https://access.redhat.com/solutions/3176
# centos7 也建议将 selinux 开关写在 cmdline
# grep selinux=0 /usr/lib/dracut/modules.d/98selinux/selinux-loadpolicy.sh
# warn "To disable selinux, add selinux=0 to the kernel command line."
if [ -f $os_dir/etc/selinux/config ]; then
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/g' $os_dir/etc/selinux/config
fi
2024-11-29 22:13:52 +08:00
chroot $os_dir grubby --update-kernel ALL --args selinux=0
# kdump
# grubby 只处理 GRUB_CMDLINE_LINUX不会处理 GRUB_CMDLINE_LINUX_DEFAULT
# rocky 的 GRUB_CMDLINE_LINUX_DEFAULT 有 crashkernel=auto
# 新安装的内核依然有 crashkernel好像是 bug
# https://forums.rockylinux.org/t/how-do-i-remove-crashkernel-from-cmdline/13346
# 验证过程
# yum remove --oldinstallonly # 删除旧内核
# rm -rf /boot/loader/entries/* # 删除启动条目
# yum reinstall kernel-core # 重新安装新内核
# cat /boot/loader/entries/* # 依然有 crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M
chroot $os_dir grubby --update-kernel ALL --args crashkernel=no
# el7 上面那条 grubby 命令不能设置 /etc/default/grub
sed -i 's/crashkernel=[^ "]*/crashkernel=no/' $os_dir/etc/default/grub
if chroot $os_dir systemctl is-enabled kdump; then
chroot $os_dir systemctl disable kdump
fi
}
2023-10-22 18:50:54 +08:00
download_qcow() {
apk add qemu-img
2024-09-06 23:00:36 +08:00
info "Download qcow2 image"
2023-07-18 00:22:51 +08:00
mkdir -p /installer
mount /dev/disk/by-label/installer /installer
2023-07-18 00:22:51 +08:00
qcow_file=/installer/cloud_image.qcow2
2024-09-06 23:00:32 +08:00
if [ -n "$img_type_warp" ]; then
# 边下载边解压,单线程下载
# 用官方 wget ,带进度条
apk add wget
wget $img -O- | pipe_extract >$qcow_file
else
2024-09-06 23:00:32 +08:00
# 多线程下载
download "$img" "$qcow_file"
fi
2023-10-22 18:50:54 +08:00
}
2023-07-28 21:27:16 +08:00
connect_qcow() {
modprobe nbd nbds_max=1
qemu-nbd -c /dev/nbd0 $qcow_file
# 需要等待一下
# https://github.com/canonical/cloud-utils/blob/main/bin/mount-image-callback
while ! blkid /dev/nbd0; do
echo "Waiting for qcow file to be mounted..."
sleep 5
done
}
disconnect_qcow() {
if [ -f /sys/block/nbd0/pid ]; then
qemu-nbd -d /dev/nbd0
# 需要等待一下
while fuser -sm $qcow_file; do
echo "Waiting for qcow file to be unmounted..."
sleep 5
done
fi
}
get_part_size_mb_for_file_size_b() {
local file_b=$1
local file_mb=$((file_b / 1024 / 1024))
# ext4 默认参数下
# 分区大小 可用大小 利用率
# 100 MiB 86 MiB 86.0%
# 200 MiB 177 MiB 88.5%
# 500 MiB 454 MiB 90.8%
# 512 MiB 476 MiB 92.9%
# 1024 MiB 957 MiB 93.4%
# 2000 MiB 1914 MiB 95.7%
# 2048 MiB 1929 MiB 94.1% 这里反而下降了
# 5120 MiB 4938 MiB 96.4%
# 文件系统大约占用 5% 空间
# 假设 1929M 的文件,计算得到需要创建 2031M 的分区
# 但是实测 2048M 的分区才能存放 1929M 的文件
# 因此预留不足 150M 时补够 150M
local reserve_mb=$((file_mb * 100 / 95 - file_mb))
if [ $reserve_mb -lt 150 ]; then
reserve_mb=150
fi
part_mb=$((file_mb + reserve_mb))
echo "File size: $file_mb MiB" >&2
echo "Part size need: $part_mb MiB" >&2
echo $part_mb
}
2024-09-06 23:00:33 +08:00
get_cloud_image_part_size() {
# 7
# https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2211.qcow2c 400m
# 8
# https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2 600m
# https://download.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud-Base.latest.x86_64.qcow2 1.8g
# https://yum.oracle.com/templates/OracleLinux/OL8/u9/x86_64/OL8U9_x86_64-kvm-b219.qcow2 1g
# https://rhel-8.10-x86_64-kvm.qcow2 1g
# 9
# https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2 1.2g
# https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2 600m
# https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 600m
# https://yum.oracle.com/templates/OracleLinux/OL9/u3/x86_64/OL9U3_x86_64-kvm-b220.qcow2 600m
# https://rhel-9.4-x86_64-kvm.qcow2 900m
# 10
# https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-latest.x86_64.qcow2 900m
# https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/cloud/nocloud_alpine-3.19.1-x86_64-uefi-cloudinit-r0.qcow2 200m
# https://kali.download/cloud-images/current/kali-linux-2024.1-cloud-genericcloud-amd64.tar.xz 200m
# https://download.opensuse.org/tumbleweed/appliances/openSUSE-Tumbleweed-Minimal-VM.x86_64-Cloud.qcow2 300m
# https://download.opensuse.org/distribution/leap/15.5/appliances/openSUSE-Leap-15.5-Minimal-VM.aarch64-Cloud.qcow2 300m
# https://mirror.fcix.net/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2 400m
# https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2 500m
# https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2 500m
# https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img 500m
# https://gentoo.osuosl.org/experimental/amd64/openstack/gentoo-openstack-amd64-systemd-latest.qcow2 800m
# openeuler 是 .qcow2.xz要解压后才知道 qcow2 大小
if [ "$distro" = openeuler ]; then
2024-08-07 21:46:45 +08:00
# openeuler 20.03 3g
if [ "$releasever" = 20.03 ]; then
2024-08-07 21:46:45 +08:00
echo 3GiB
else
echo 2GiB
fi
2024-09-06 23:00:33 +08:00
elif size_bytes=$(get_http_file_size "$img"); then
# 缩小 btrfs 需要写 qcow2 ,实测写入后只多了 1M因此不用特殊处理
echo "$(get_part_size_mb_for_file_size_b $size_bytes)MiB"
else
# 如果没获取到文件大小
echo "Could not get cloud image size in http response." >&2
echo 2GiB
fi
2024-04-27 09:45:10 +08:00
}
chroot_dnf() {
if is_have_cmd_on_disk /os/ dnf; then
chroot /os/ dnf -y "$@"
2024-05-03 23:34:42 +08:00
else
chroot /os/ yum -y "$@"
2024-05-03 23:34:42 +08:00
fi
}
2023-07-28 21:27:16 +08:00
chroot_apt_install() {
os_dir=$1
shift
current_hash=$(cat $os_dir/etc/apt/sources.list $os_dir/etc/apt/sources.list.d/*.sources 2>/dev/null | md5sum)
if ! [ "$saved_hash" = "$current_hash" ]; then
chroot $os_dir apt-get update
saved_hash="$current_hash"
fi
DEBIAN_FRONTEND=noninteractive chroot $os_dir apt-get install -y "$@"
}
chroot_apt_autoremove() {
os_dir=$1
change_confs() {
action=$1
# 只有 16.04 有 01autoremove-kernels
# 16.04 结束支持后删除
for conf in 01autoremove 01autoremove-kernels; do
file=$os_dir/etc/apt/apt.conf.d/$conf
case "$action" in
change)
if [ -f $file ]; then
sed -i.orig 's/VersionedKernelPackages/x/; s/NeverAutoRemove/x/' $file
fi
;;
restore)
if [ -f $file.orig ]; then
mv $file.orig $file
fi
;;
esac
done
}
change_confs change
2024-09-06 23:00:35 +08:00
DEBIAN_FRONTEND=noninteractive chroot $os_dir apt-get autoremove --purge -y
change_confs restore
}
del_default_user() {
os_dir=$1
while read -r user; do
if grep ^$user':\$' "$os_dir/etc/shadow"; then
echo "Deleting user $user"
chroot "$os_dir" userdel -rf "$user"
fi
done < <(grep -v nologin$ "$os_dir/etc/passwd" | cut -d: -f1 | grep -v root)
}
is_el7_family() {
is_have_cmd_on_disk "$1" yum &&
! is_have_cmd_on_disk "$1" dnf
}
fix_sysconfig_NetworkManager() {
os_dir=$1
ci_file=$os_dir/etc/cloud/cloud.cfg.d/99_fallback.cfg
# 删除云镜像自带的 dhcp 配置,防止歧义
rm -rf $os_dir/etc/NetworkManager/system-connections/*.nmconnection
rm -rf $os_dir/etc/sysconfig/network-scripts/ifcfg-*
# 1. 修复 cloud-init 添加了 IPV*_FAILURE_FATAL / may-fail=false
# 甲骨文 dhcpv6 获取不到 IP 将视为 fatal原有的 ipv4 地址也会被删除
# 2. 修复 dhcpv6 下ifcfg 添加了 IPV6_AUTOCONF=no 导致无法获取网关
# 3. 修复 dhcpv6 下NM method=dhcp 导致无法获取网关
insert_into_file $ci_file after '^runcmd:' <<EOF
- sed -i '/^IPV[46]_FAILURE_FATAL=/d' /etc/sysconfig/network-scripts/ifcfg-* || true
- sed -i '/^may-fail=/d' /etc/NetworkManager/system-connections/*.nmconnection || true
- for f in /etc/sysconfig/network-scripts/ifcfg-*; do grep -q '^DHCPV6C=yes' "\$f" && sed -i '/^IPV6_AUTOCONF=no/d' "\$f"; done
- sed -i 's/^method=dhcp/method=auto/' /etc/NetworkManager/system-connections/*.nmconnection || true
- systemctl is-enabled NetworkManager && systemctl restart NetworkManager || true
EOF
}
2024-05-03 23:34:42 +08:00
install_qcow_by_copy() {
2024-09-06 23:00:36 +08:00
info "Install qcow2 by copy"
2024-04-27 09:45:10 +08:00
efi_mount_opts=$(
case "$distro" in
ubuntu) echo "umask=0077" ;;
*) echo "defaults,uid=0,gid=0,umask=077,shortname=winnt" ;;
esac
)
connect_qcow
2024-05-03 23:34:42 +08:00
# 镜像分区格式
2024-11-26 22:09:05 +08:00
# centos/rocky/almalinux/rhel: xfs
# oracle x86_64: lvm + xfs
# oracle aarch64 cloud: xfs
# alibaba cloud linux 3: ext4
is_lvm_image=false
2024-05-03 23:34:42 +08:00
if lsblk -f /dev/nbd0p* | grep LVM2_member; then
is_lvm_image=true
2024-05-03 23:34:42 +08:00
apk add lvm2
lvscan
vg=$(pvs | grep /dev/nbd0p | awk '{print $2}')
lvchange -ay "$vg"
fi
# TODO: 系统分区应该是最后一个分区
# 选择最大分区
os_part=$(lsblk /dev/nbd0p* --sort SIZE -no NAME,FSTYPE | grep -E 'ext4|xfs' | tail -1 | awk '{print $1}')
efi_part=$(lsblk /dev/nbd0p* --sort SIZE -no NAME,PARTTYPE | grep -i "$EFI_UUID" | awk '{print $1}')
# 排除前两个,再选择最大分区
2024-11-26 22:09:05 +08:00
# almalinux9 boot 分区的类型不是规定的 uuid
# openeuler boot 分区是 fat 格式
boot_part=$(lsblk /dev/nbd0p* --sort SIZE -no NAME,FSTYPE | grep -E 'ext4|xfs|fat' | awk '{print $1}' |
2024-08-07 21:46:45 +08:00
grep -vx "$os_part" | grep -vx "$efi_part" | tail -1 | awk '{print $1}')
if $is_lvm_image; then
os_part="mapper/$os_part"
2024-05-03 23:34:42 +08:00
fi
2023-07-28 21:27:16 +08:00
info "qcow2 Partitions"
lsblk -f /dev/nbd0 -o +PARTTYPE
echo "Part OS: $os_part"
echo "Part EFI: $efi_part"
echo "Part Boot: $boot_part"
# 分区寻找方式
# 系统/分区 cmdline:root fstab:efi
# rocky LABEL=rocky LABEL=EFI
# ubuntu PARTUUID LABEL=UEFI
# 其他el/ol UUID UUID
# read -r os_part_uuid os_part_label < <(lsblk /dev/$os_part -no UUID,LABEL)
2023-10-22 18:50:54 +08:00
os_part_uuid=$(lsblk /dev/$os_part -no UUID)
os_part_label=$(lsblk /dev/$os_part -no LABEL)
os_part_fstype=$(lsblk /dev/$os_part -no FSTYPE)
2023-10-22 18:50:54 +08:00
if [ -n "$efi_part" ]; then
efi_part_uuid=$(lsblk /dev/$efi_part -no UUID)
efi_part_label=$(lsblk /dev/$efi_part -no LABEL)
2023-10-22 18:50:54 +08:00
fi
2023-07-28 21:27:16 +08:00
mkdir -p /nbd /nbd-boot /nbd-efi
2023-10-22 18:50:54 +08:00
mount_nouuid() {
case "$os_part_fstype" in
ext4) mount "$@" ;;
xfs) mount -o nouuid "$@" ;;
esac
}
2023-10-22 18:50:54 +08:00
# 使用目标系统的格式化程序
# centos8 如果用alpine格式化xfsgrub2-mkconfig和grub2里面都无法识别xfs分区
2024-04-27 09:45:10 +08:00
mount_nouuid /dev/$os_part /nbd/
2023-10-22 18:50:54 +08:00
mount_pseudo_fs /nbd/
case "$os_part_fstype" in
2024-10-01 20:05:18 +08:00
ext4) chroot /nbd mkfs.ext4 -F -L "$os_part_label" -U "$os_part_uuid" /dev/$xda*2 ;;
xfs) chroot /nbd mkfs.xfs -f -L "$os_part_label" -m uuid=$os_part_uuid /dev/$xda*2 ;;
2024-04-27 09:45:10 +08:00
esac
2023-10-22 18:50:54 +08:00
umount -R /nbd/
2024-04-27 09:45:10 +08:00
# TODO: ubuntu 镜像缺少 mkfs.fat/vfat/dosfstools? initrd 不需要检查fs完整性
# 创建并挂载 /os
mkdir -p /os
mount -o noatime /dev/$xda*2 /os/
# 如果是 efi 则创建 /os/boot/efi
# 如果镜像有 efi 分区也创建 /os/boot/efi用于复制 efi 分区的文件
if is_efi || [ -n "$efi_part" ]; then
mkdir -p /os/boot/efi/
# 挂载 /os/boot/efi
# 预先挂载 /os/boot/efi 因为可能 boot 和 efi 在同一个分区openeuler 24.03 arm
# 复制 boot 时可以会复制 efi 的文件
if is_efi; then
mount -o $efi_mount_opts /dev/$xda*1 /os/boot/efi/
fi
fi
# 复制系统分区
2024-04-27 09:45:10 +08:00
echo Copying os partition...
mount_nouuid -o ro /dev/$os_part /nbd/
2023-10-22 18:50:54 +08:00
cp -a /nbd/* /os/
umount /nbd/
2023-10-22 18:50:54 +08:00
# 复制boot分区如果有
if [ -n "$boot_part" ]; then
2024-04-27 09:45:10 +08:00
echo Copying boot partition...
mount_nouuid -o ro /dev/$boot_part /nbd-boot/
2023-10-22 18:50:54 +08:00
cp -a /nbd-boot/* /os/boot/
umount /nbd-boot/
2023-10-22 18:50:54 +08:00
fi
# 复制efi分区如果有
if [ -n "$efi_part" ]; then
echo Copying efi partition...
mount -o ro /dev/$efi_part /nbd-efi/
cp -a /nbd-efi/* /os/boot/efi/
umount /nbd-efi/
2023-10-22 18:50:54 +08:00
fi
# 断开 qcow
2024-05-03 23:34:42 +08:00
if is_have_cmd vgchange; then
vgchange -an
fi
disconnect_qcow
2023-10-22 18:50:54 +08:00
2024-04-28 00:02:31 +08:00
# 已复制并断开连接 qcow可删除 qemu-img
apk del qemu-img
2023-10-22 18:50:54 +08:00
# 如果镜像有efi分区复制其uuid
# 如果有相同uuid的fat分区则无法挂载
# 所以要先复制efi分区断开nbd再复制uuid
if is_efi && [ -n "$efi_part_uuid" ]; then
umount /os/boot/efi/
apk add mtools
mlabel -N "$(echo $efi_part_uuid | sed 's/-//')" -i /dev/$xda*1 ::$efi_part_label
update_part
2023-10-22 18:50:54 +08:00
mount -o $efi_mount_opts /dev/$xda*1 /os/boot/efi/
fi
2023-10-22 18:50:54 +08:00
# 挂载伪文件系统
mount_pseudo_fs /os/
2023-10-22 18:50:54 +08:00
# 创建 swap
2024-04-27 09:45:10 +08:00
umount /installer/
mkswap /dev/$xda*3
swapon /dev/$xda*3
2023-08-03 22:38:38 +08:00
2024-05-03 23:34:42 +08:00
modify_el_ol() {
2024-09-06 23:00:36 +08:00
info "Modify el ol"
2024-04-27 09:45:10 +08:00
# resolv.conf
2024-05-22 22:32:43 +08:00
cp_resolv_conf /os
2024-04-27 09:45:10 +08:00
# 删除镜像的默认账户,防止使用默认账户密码登录 ssh
del_default_user /os
2024-04-27 09:45:10 +08:00
# selinux kdump
disable_selinux_kdump /os
# el7 删除 machine-id 后不会自动重建
clear_machine_id /os
# el7 forks 特殊处理
if is_el7_family /os; then
# centos 7 eol 换源
if [ -f /os/etc/yum.repos.d/CentOS-Base.repo ]; then
# 保持默认的 http 因为自带的 ssl 证书可能过期
if is_in_china; then
mirror=mirror.nju.edu.cn/centos-vault
else
mirror=vault.centos.org
fi
sed -Ei -e 's,(mirrorlist=),#\1,' \
-e "s,#(baseurl=http://)mirror.centos.org,\1$mirror," /os/etc/yum.repos.d/CentOS-Base.repo
fi
# el7 yum 可能会使用 ipv6即使没有 ipv6 网络
if [ "$(cat /dev/netconf/eth*/ipv6_has_internet | sort -u)" = 0 ]; then
echo 'ip_resolve=4' >>/os/etc/yum.conf
fi
# el7 安装 NetworkManager
# anolis 7 镜像自带 NetworkManager
chroot_dnf install NetworkManager
chroot /os systemctl disable network
chroot /os systemctl enable NetworkManager
2024-07-14 22:12:59 +08:00
fi
# firmware + microcode
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot_dnf install $fw_pkgs
fi
# 修复 cloud-init + dhcp 的各种网络问题
fix_sysconfig_NetworkManager /os
2024-05-03 23:34:42 +08:00
# fstab 删除多余分区
2024-11-26 22:09:05 +08:00
# almalinux/rocky 镜像有 boot 分区
2024-05-03 23:34:42 +08:00
# oracle 镜像有 swap 分区
2024-04-27 09:45:10 +08:00
sed -i '/[[:space:]]\/boot[[:space:]]/d' /os/etc/fstab
2024-05-03 23:34:42 +08:00
sed -i '/[[:space:]]swap[[:space:]]/d' /os/etc/fstab
# os_part 变量:
# mapper/vg_main-lv_root
# mapper/opencloudos-root
# oracle/opencloudos 系统盘从 lvm 改成 uuid 挂载
sed -i "s,/dev/$os_part,UUID=$os_part_uuid," /os/etc/fstab
2024-05-03 23:34:42 +08:00
if ls /os/boot/loader/entries/*.conf 2>/dev/null; then
# options root=/dev/mapper/opencloudos-root ro console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=1800M-64G:256M,64G-128G:512M,128G-486G:768M,486G-972G:1024M,972G-:2048M rd.lvm.lv=opencloudos/root rhgb quiet
sed -i "s,/dev/$os_part,UUID=$os_part_uuid," /os/boot/loader/entries/*.conf
2024-05-03 23:34:42 +08:00
fi
# oracle/opencloudos 移除 lvm cmdline
2024-05-03 23:34:42 +08:00
chroot /os grubby --update-kernel ALL --remove-args "resume rd.lvm.lv"
# el7 上面那条 grubby 命令不能设置 /etc/default/grub
sed -i 's/rd.lvm.lv=[^ "]*//g' /os/etc/default/grub
2023-10-22 18:50:54 +08:00
2024-04-27 09:45:10 +08:00
# fstab 添加 efi 分区
if is_efi; then
2024-05-03 23:34:42 +08:00
# centos/oracle 要创建efi条目
2024-04-27 09:45:10 +08:00
if ! grep /boot/efi /os/etc/fstab; then
efi_part_uuid=$(lsblk /dev/$xda*1 -no UUID)
echo "UUID=$efi_part_uuid /boot/efi vfat $efi_mount_opts 0 0" >>/os/etc/fstab
fi
else
# 删除 efi 条目
sed -i '/[[:space:]]\/boot\/efi[[:space:]]/d' /os/etc/fstab
fi
2024-04-27 09:45:10 +08:00
remove_grub_conflict_files() {
# bios 和 efi 转换前先删除
2023-07-30 00:35:32 +08:00
2024-04-27 09:45:10 +08:00
# bios转efi出错
# centos 和 oracle x86_64 镜像只有 bios 镜像,/boot/grub2/grubenv 是真身
2024-04-27 09:45:10 +08:00
# 安装grub-efi时grubenv 会改成指向efi分区grubenv软连接
# 如果安装grub-efi前没有删除原来的grubenv原来的grubenv将不变新建的软连接将变成 grubenv.rpmnew
# 后续grubenv的改动无法同步到efi分区会造成grub2-setdefault失效
2023-07-30 00:35:32 +08:00
2024-04-27 09:45:10 +08:00
# efi转bios出错
# 如果是指向efi目录的软连接例如el8先删除它否则 grub2-install 会报错
rm -rf /os/boot/grub2/grubenv /os/boot/grub2/grub.cfg
}
2023-07-30 00:35:32 +08:00
# openeuler arm 镜像 grub.cfg 在 /os/grub.cfg可能给外部的 grub 读取,我们用不到
# centos7 有 grub1 的配置
rm -rf /os/grub.cfg /os/boot/grub/grub.conf /os/boot/grub/menu.lst
# 安装引导
if is_efi; then
# 只有centos 和 oracle x86_64 镜像没有efi其他系统镜像已经从efi分区复制了文件
if [ -z "$efi_part" ]; then
remove_grub_conflict_files
# openeuler 自带 grub2-efi-ia32此时安装 grub2-efi 提示已经安装了 grub2-efi-ia32不会继续安装 grub2-efi-x64
[ "$(uname -m)" = x86_64 ] && arch=x64 || arch=aa64
chroot_dnf install efibootmgr grub2-efi-$arch shim-$arch
fi
else
# bios
2024-04-27 09:45:10 +08:00
remove_grub_conflict_files
chroot /os/ grub2-install /dev/$xda
fi
2023-07-28 21:27:16 +08:00
2024-04-27 09:45:10 +08:00
# blscfg 启动项
2024-11-26 22:09:05 +08:00
# rocky/almalinux镜像是独立的boot分区但我们不是
2024-04-27 09:45:10 +08:00
# 因此要添加boot目录
if ls /os/boot/loader/entries/*.conf 2>/dev/null &&
! grep -q 'initrd /boot/' /os/boot/loader/entries/*.conf; then
2024-04-27 09:45:10 +08:00
sed -i -E 's,((linux|initrd) /),\1boot/,g' /os/boot/loader/entries/*.conf
fi
2023-07-28 21:27:16 +08:00
# grub-efi-x64 包里面有 /etc/grub2-efi.cfg
# 指向 /boot/efi/EFI/xxx/grub.cfg 或 /boot/grub2/grub.cfg
# 指向哪里哪里就是 grub2-mkconfig 应该生成文件的位置
# grubby 也是靠 /etc/grub2-efi.cfg 定位 grub.cfg 的位置
# openeuler 24.03 x64 aa64 指向的文件不同
2024-05-03 23:34:42 +08:00
if is_efi; then
grub_o_cfg=$(chroot /os readlink -f /etc/grub2-efi.cfg)
else
grub_o_cfg=/boot/grub2/grub.cfg
2024-05-03 23:34:42 +08:00
fi
2024-04-27 09:45:10 +08:00
# efi 分区 grub.cfg
# https://github.com/rhinstaller/anaconda/blob/346b932a26a19b339e9073c049b08bdef7f166c3/pyanaconda/modules/storage/bootloader/efi.py#L198
# https://github.com/rhinstaller/anaconda/commit/15c3b2044367d375db6739e8b8f419ef3e17cae7
if is_efi && ! echo "$grub_o_cfg" | grep -q '/boot/efi/EFI'; then
# oracle linux 文件夹是 redhat
# shellcheck disable=SC2010
distro_efi=$(cd /os/boot/efi/EFI/ && ls -d -- * | grep -Eiv BOOT)
2024-05-03 23:34:42 +08:00
cat <<EOF >/os/boot/efi/EFI/$distro_efi/grub.cfg
2024-04-27 09:45:10 +08:00
search --no-floppy --fs-uuid --set=dev $os_part_uuid
set prefix=(\$dev)/boot/grub2
export \$prefix
configfile \$prefix/grub.cfg
2023-07-30 00:35:32 +08:00
EOF
2024-04-27 09:45:10 +08:00
fi
2023-07-30 00:35:32 +08:00
2024-04-27 09:45:10 +08:00
# 主 grub.cfg
# --update-bls-cmdline
chroot /os/ grub2-mkconfig -o "$grub_o_cfg"
2024-04-27 09:45:10 +08:00
# 不删除可能网络管理器不会写入dns
2024-05-22 22:32:43 +08:00
rm_resolv_conf /os
2024-04-27 09:45:10 +08:00
}
modify_ubuntu() {
os_dir=/os
2024-09-06 23:00:36 +08:00
info "Modify Ubuntu"
2024-04-27 09:45:10 +08:00
2024-05-22 22:32:43 +08:00
cp_resolv_conf $os_dir
2024-04-27 09:45:10 +08:00
# 关闭 os prober因为 os prober 有时很慢
cp $os_dir/etc/default/grub $os_dir/etc/default/grub.orig
echo 'GRUB_DISABLE_OS_PROBER=true' >>$os_dir/etc/default/grub
# 更改源
if is_in_china; then
# 22.04 使用 /etc/apt/sources.list
# 24.04 使用 /etc/apt/sources.list.d/ubuntu.sources
for file in $os_dir/etc/apt/sources.list $os_dir/etc/apt/sources.list.d/ubuntu.sources; do
if [ -f $file ]; then
# cn.archive.ubuntu.com 不在国内还严重丢包
# https://www.itdog.cn/ping/cn.archive.ubuntu.com
sed -i 's/archive.ubuntu.com/mirror.nju.edu.cn/' $file # x64
sed -i 's/ports.ubuntu.com/mirror.nju.edu.cn/' $file # arm
fi
done
2024-04-27 09:45:10 +08:00
fi
# 16.04 arm64 镜像没有 grub 引导文件
if is_efi && ! [ -d $os_dir/boot/efi/EFI/ubuntu ]; then
DEBIAN_FRONTEND=noninteractive chroot $os_dir \
apt-get upgrade --reinstall -y efibootmgr shim "grub-efi-$(get_axx64)"
cat <<EOF >"$os_dir/boot/efi/EFI/ubuntu/grub.cfg"
search.fs_uuid $os_part_uuid root
set prefix=(\$root)'/boot/grub'
configfile \$prefix/grub.cfg
EOF
fi
# 更新包索引
chroot $os_dir apt-get update
2024-04-27 09:45:10 +08:00
# 安装最佳内核
flavor=$(get_ubuntu_kernel_flavor)
echo "Use kernel flavor: $flavor"
chroot_apt_install $os_dir "linux-image-$flavor"
2024-04-27 09:45:10 +08:00
# 自带内核:
# 常规版本 generic
# minimal 20.04/22.04 kvm # 后台 vnc 无显示
# minimal 24.04 virtual
2024-05-03 23:34:42 +08:00
# debian cloud 内核不支持 ahciubuntu virtual 支持
2024-04-27 09:45:10 +08:00
# 标记旧内核包
# 注意排除 linux-base
if pkgs=$(chroot $os_dir apt-mark showmanual linux-* | grep -E 'generic|virtual|kvm' | grep -v $flavor); then
chroot $os_dir apt-mark auto $pkgs
2024-04-27 09:45:10 +08:00
# 使用 autoremove
chroot_apt_autoremove $os_dir
fi
# 安装固件+微码
2024-12-06 22:50:05 +08:00
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot_apt_install $os_dir $fw_pkgs
fi
# 16.04 镜像用 ifupdown/networking 管理网络
# 要安装 resolveconf不然 /etc/resolv.conf 为空
if [ "$releasever" = 16.04 ]; then
chroot_apt_install $os_dir resolvconf
ln -sf /run/resolvconf/resolv.conf $os_dir/etc/resolv.conf.orig
fi
2024-04-27 09:45:10 +08:00
# 安装 bios 引导
if ! is_efi; then
chroot $os_dir grub-install /dev/$xda
fi
# 更改 efi 目录的 grub.cfg 写死的 fsuuid
# 因为 24.04 fsuuid 对应 boot 分区
efi_grub_cfg=$os_dir/boot/efi/EFI/ubuntu/grub.cfg
if is_efi; then
os_uuid=$(lsblk -rno UUID /dev/$xda*2)
sed -Ei "s|[0-9a-f-]{36}|$os_uuid|i" $efi_grub_cfg
# 24.04 移除 boot 分区后,需要添加 /boot 路径
if grep "'/grub'" $efi_grub_cfg; then
sed -i "s|'/grub'|'/boot/grub'|" $efi_grub_cfg
fi
fi
# 处理 40-force-partuuid.cfg
force_partuuid_cfg=$os_dir/etc/default/grub.d/40-force-partuuid.cfg
if [ -e $force_partuuid_cfg ]; then
if is_virt; then
# 更改写死的 partuuid
os_part_uuid=$(lsblk -rno PARTUUID /dev/$xda*2)
sed -i "s/^GRUB_FORCE_PARTUUID=.*/GRUB_FORCE_PARTUUID=$os_part_uuid/" $force_partuuid_cfg
else
# 独服不应该使用 initrdless boot
sed -i "/^GRUB_FORCE_PARTUUID=/d" $force_partuuid_cfg
fi
fi
2023-05-03 22:22:21 +08:00
2024-04-27 09:45:10 +08:00
# 要重新生成 grub.cfg因为
# 1 我们删除了 boot 分区
# 2 改动了 /etc/default/grub.d/40-force-partuuid.cfg
chroot $os_dir update-grub
# 还原 grub 配置os prober
mv $os_dir/etc/default/grub.orig $os_dir/etc/default/grub
# fstab
# 24.04 镜像有boot分区但我们不需要
sed -i '/[[:space:]]\/boot[[:space:]]/d' $os_dir/etc/fstab
if ! is_efi; then
# bios 删除 efi 条目
sed -i '/[[:space:]]\/boot\/efi[[:space:]]/d' $os_dir/etc/fstab
fi
restore_resolv_conf $os_dir
2024-04-27 09:45:10 +08:00
}
# anolis/openeuler/opencloudos 可能要安装 cloud-init
# opencloudos 无法使用 chroot $os_dir command -v xxx
# chroot: failed to run command command: No such file or directory
if is_have_cmd_on_disk $os_dir rpm &&
! is_have_cmd_on_disk $os_dir cloud-init; then
cp_resolv_conf $os_dir
chroot_dnf install cloud-init
restore_resolv_conf $os_dir
fi
# cloud-init 路径
# /usr/lib/python2.7/site-packages/cloudinit/net/
# /usr/lib/python3/dist-packages/cloudinit/net/
# /usr/lib/python3.9/site-packages/cloudinit/net/
# el7 不认识 static6但可改成 static作用相同
recognize_static6=true
if ls $os_dir/usr/lib/python*/*-packages/cloudinit/net/sysconfig.py 2>/dev/null &&
! grep -q static6 $os_dir/usr/lib/python*/*-packages/cloudinit/net/sysconfig.py; then
recognize_static6=false
fi
# cloud-init 20.1 才支持以下配置
# https://cloudinit.readthedocs.io/en/20.4/topics/network-config-format-v1.html#subnet-ip
# https://cloudinit.readthedocs.io/en/21.1/topics/network-config-format-v1.html#subnet-ip
# ipv6_dhcpv6-stateful: Configure this interface with dhcp6
# ipv6_dhcpv6-stateless: Configure this interface with SLAAC and DHCP
# ipv6_slaac: Configure address with SLAAC
# el7 最新 cloud-init 版本
# centos 7 19.4-7.0.5.el7_9.6 backport 了 ipv6_xxx
# openeuler 20.03 19.4-15.oe2003sp4 backport 了 ipv6_xxx
# anolis 7 19.1.17-1.0.1.an7 没有更新到 centos7 相同版本,也没 backport ipv6_xxx
# 最好还修改 ifcfg-eth* 的 IPV6_AUTOCONF
# 但实测 anolis7 cloud-init dhcp6 不会生成 IPV6_AUTOCONF因此暂时不管
# https://www.redhat.com/zh/blog/configuring-ipv6-rhel-7-8
recognize_ipv6_types=true
if ls -d $os_dir/usr/lib/python*/*-packages/cloudinit/net/ 2>/dev/null &&
! grep -qr ipv6_slaac $os_dir/usr/lib/python*/*-packages/cloudinit/net/; then
recognize_ipv6_types=false
fi
# cloud-init
download_cloud_init_config "$os_dir" "$recognize_static6" "$recognize_ipv6_types"
2024-04-27 09:45:10 +08:00
case "$distro" in
ubuntu) modify_ubuntu ;;
2024-05-03 23:34:42 +08:00
*) modify_el_ol ;;
2024-04-27 09:45:10 +08:00
esac
# 查看最终的 cloud-init 配置
cat /os/etc/cloud/cloud.cfg.d/99_*.cfg
2023-10-22 18:50:54 +08:00
# 删除installer分区重启后cloud init会自动扩容
swapoff -a
parted /dev/$xda -s rm 3
}
2023-07-30 00:35:32 +08:00
2024-10-18 23:58:03 +08:00
get_partition_table_format() {
apk add parted
parted "$1" -s print | grep 'Partition Table:' | awk '{print $NF}'
}
2023-10-22 18:50:54 +08:00
dd_qcow() {
2024-09-06 23:00:36 +08:00
info "DD qcow2"
2023-10-22 18:50:54 +08:00
if true; then
connect_qcow
2023-10-22 18:50:54 +08:00
2024-10-18 23:58:03 +08:00
partition_table_format=$(get_partition_table_format /dev/nbd0)
orig_nbd_virtual_size=$(get_disk_size /dev/nbd0)
2023-10-22 18:50:54 +08:00
# 检查最后一个分区是否是 btrfs
# 即使awk结果为空返回值也是0加上 grep . 检查是否结果为空
if part_num=$(parted /dev/nbd0 -s print | awk NF | tail -1 | grep btrfs | awk '{print $1}' | grep .); then
apk add btrfs-progs
mkdir -p /mnt/btrfs
mount /dev/nbd0p$part_num /mnt/btrfs
# 回收空数据块
btrfs device usage /mnt/btrfs
btrfs balance start -dusage=0 /mnt/btrfs
btrfs device usage /mnt/btrfs
# 计算可以缩小的空间
free_bytes=$(btrfs device usage /mnt/btrfs -b | grep Unallocated: | awk '{print $2}')
reserve_bytes=$((100 * 1024 * 1024)) # 预留 100M 可用空间
skrink_bytes=$((free_bytes - reserve_bytes))
if [ $skrink_bytes -gt 0 ]; then
# 缩小文件系统
btrfs filesystem resize -$skrink_bytes /mnt/btrfs
# 缩小分区
part_start=$(parted /dev/nbd0 -s 'unit b print' | awk "\$1==$part_num {print \$2}" | sed 's/B//')
part_size=$(btrfs filesystem usage /mnt/btrfs -b | grep 'Device size:' | awk '{print $3}')
part_end=$((part_start + part_size - 1))
2023-10-22 18:50:54 +08:00
umount /mnt/btrfs
printf "yes" | parted /dev/nbd0 resizepart $part_num ${part_end}B ---pretend-input-tty
# 缩小 qcow2
disconnect_qcow
qemu-img resize --shrink $qcow_file $((part_end + 1))
2023-10-22 18:50:54 +08:00
# 重新连接
connect_qcow
2023-10-22 18:50:54 +08:00
else
umount /mnt/btrfs
2023-08-01 21:51:36 +08:00
fi
2023-10-22 18:50:54 +08:00
fi
2023-08-01 21:51:36 +08:00
2023-10-22 18:50:54 +08:00
# 显示分区
lsblk -o NAME,SIZE,FSTYPE,LABEL /dev/nbd0
# 将前1M dd到内存
dd if=/dev/nbd0 of=/first-1M bs=1M count=1
# 将1M之后 dd到硬盘
# shellcheck disable=SC2194
case 3 in
1)
# BusyBox dd
dd if=/dev/nbd0 of=/dev/$xda bs=1M skip=1 seek=1
;;
2)
# 用原版 dd status=progress但没有进度和剩余时间
apk add coreutils
dd if=/dev/nbd0 of=/dev/$xda bs=1M skip=1 seek=1 status=progress
;;
3)
# 用 pv
apk add pv
2023-10-22 19:07:12 +08:00
echo "Start DD Cloud Image..."
2023-10-22 18:50:54 +08:00
pv -f /dev/nbd0 | dd of=/dev/$xda bs=1M skip=1 seek=1 iflag=fullblock
;;
esac
disconnect_qcow
2023-10-22 18:50:54 +08:00
else
# 将前1M dd到内存将1M之后 dd到硬盘
qemu-img dd if=$qcow_file of=/first-1M bs=1M count=1
qemu-img dd if=$qcow_file of=/dev/disk/by-label/os bs=1M skip=1
fi
2023-07-18 00:22:51 +08:00
2024-04-28 00:02:31 +08:00
# 已 dd 并断开连接 qcow可删除 qemu-img
apk del qemu-img
2023-10-22 18:50:54 +08:00
# 将前1M从内存 dd 到硬盘
umount /installer/
dd if=/first-1M of=/dev/$xda
2024-10-18 23:58:03 +08:00
# gpt 分区表开头记录了备份分区表的位置
# 如果 qcow2 虚拟容量 大于 实际硬盘容量
# 备份分区表的位置 将超出实际硬盘容量的大小
# partprobe 会报错
# Error: Invalid argument during seek for read on /dev/vda
# parted 也无法正常工作
# 需要提前修复分区表
# 目前只有这个例子,因为其他 qcow2 虚拟容量最多 5g是设定支持的容量
# openSUSE-Leap-15.5-Minimal-VM.x86_64-kvm-and-xen.qcow2 容量是 25g
# 缩小 btrfs 分区后 dd 到 10g 的机器上
# 备份分区表的位置是 25g
# 需要修复到 10g 的位置上
# 否则 partprobe parted 都无法正常工作
# 仅这种情况才用 sgdisk 修复
if [ "$partition_table_format" = gpt ] &&
[ "$orig_nbd_virtual_size" -gt "$(get_disk_size /dev/$xda)" ]; then
fix_gpt_backup_partition_table_by_sgdisk
fi
update_part
}
fix_gpt_backup_partition_table_by_sgdisk() {
# 当备份分区表超出实际硬盘容量时,只能用 sgdisk 修复分区表
# 应用场景:镜像大小超出硬盘实际硬盘,但缩小分区后不超出实际硬盘容量,可以顺利 DD
# 例子 openSUSE-Leap-15.5-Minimal-VM.x86_64-kvm-and-xen.qcow2
# parted 无法修复
# parted /dev/$xda -f -s print
# fdisk/sfdisk 显示主分区表损坏
# echo write | sfdisk /dev/$xda
# GPT PMBR size mismatch (50331647 != 20971519) will be corrected by write.
# The primary GPT table is corrupt, but the backup appears OK, so that will be used.
# 除此之外的场景应该用 parted 来修复
apk add sgdisk
# 两种方法都可以,但都不会修复备份分区表的 GUID
# 此时 sgdisk -v /dev/vda 会提示主副分区表 guid 不相同
# localhost:~# sgdisk -v /dev/$xda
# Problem: main header's disk GUID (A24485F3-2C02-43BD-BF4E-F52E42B00DEA) doesn't
# match the backup GPT header's disk GUID (ADAF57BC-B4F5-4E04-BCBA-BDDCD796C388)
# You should use the 'b' or 'd' option on the recovery & transformation menu to
# select one or the other header.
if false; then
sgdisk --backup /gpt-partition-table /dev/$xda
sgdisk --load-backup /gpt-partition-table /dev/$xda
else
sgdisk --move-second-header /dev/$xda
fi
# 因此需要运行一次设置 guid
if new_guid=$(sgdisk -v /dev/$xda | grep GUID | head -1 | grep -Eo '[0-9A-F-]{36}'); then
sgdisk --disk-guid $new_guid /dev/$xda
fi
update_part
2023-07-22 22:02:33 +08:00
2024-10-18 23:58:03 +08:00
apk del sgdisk
2023-10-22 18:50:54 +08:00
}
2023-09-16 20:00:07 +08:00
2024-10-18 23:58:03 +08:00
# 适用于 DD 后修复 gpt 备份分区表
fix_gpt_backup_partition_table_by_parted() {
2024-03-05 23:01:24 +08:00
parted /dev/$xda -f -s print
2024-10-18 23:58:03 +08:00
update_part
2024-03-05 23:01:24 +08:00
}
2023-10-22 18:50:54 +08:00
resize_after_install_cloud_image() {
# 提前扩容
# 1 修复 vultr 512m debian 11 generic/genericcloud 首次启动 kernel panic
2024-10-18 23:58:03 +08:00
# 2 防止 gentoo 云镜像 websync 时空间不足
info "Resize after dd"
lsblk -f /dev/$xda
2023-10-22 18:50:54 +08:00
2024-10-18 23:58:03 +08:00
# 打印分区表,并自动修复备份分区表
fix_gpt_backup_partition_table_by_parted
disk_size=$(get_disk_size /dev/$xda)
disk_end=$((disk_size - 1))
# 不能漏掉最后的 _ 否则第6部分都划到给 last_part_fs
IFS=: read -r last_part_num _ last_part_end _ last_part_fs _ \
< <(parted -msf /dev/$xda 'unit b print' | tail -1)
last_part_end=$(echo $last_part_end | sed 's/B//')
# 大于 100M 才扩容
if [ $((disk_end - last_part_end)) -ge $((100 * 1024 * 1024)) ]; then
printf "yes" | parted /dev/$xda resizepart $last_part_num 100% ---pretend-input-tty
update_part
mkdir -p /os
# lvm ?
# 用 cloud-utils-growpart
case "$last_part_fs" in
ext4)
# debian ci
apk add e2fsprogs-extra
e2fsck -p -f /dev/$xda*$last_part_num
resize2fs /dev/$xda*$last_part_num
apk del e2fsprogs-extra
;;
xfs)
# opensuse ci
apk add xfsprogs-extra
mount /dev/$xda*$last_part_num /os
xfs_growfs /dev/$xda*$last_part_num
umount /os
apk del xfsprogs-extra
;;
btrfs)
# fedora ci
apk add btrfs-progs
mount /dev/$xda*$last_part_num /os
btrfs filesystem resize max /os
umount /os
apk del btrfs-progs
;;
ntfs)
# windows dd
apk add ntfs-3g-progs
echo y | ntfsresize /dev/$xda*$last_part_num
ntfsfix -d /dev/$xda*$last_part_num
apk del ntfs-3g-progs
;;
esac
update_part
parted /dev/$xda -s print
fi
2023-07-25 00:21:08 +08:00
}
2024-08-19 00:33:59 +08:00
mount_part_basic_layout() {
os_dir=$1
efi_dir=$2
if is_efi || is_xda_gt_2t; then
os_part_num=2
else
os_part_num=1
fi
# 挂载系统分区
mkdir -p $os_dir
mount -t ext4 /dev/${xda}*${os_part_num} $os_dir
# 挂载 efi 分区
if is_efi; then
mkdir -p $efi_dir
mount -t vfat -o umask=077 /dev/${xda}*1 $efi_dir
fi
}
mount_part_for_iso_installer() {
2024-09-06 23:00:36 +08:00
info "Mount part for iso installer"
2024-11-01 21:17:25 +08:00
if [ "$distro" = windows ]; then
mount_args="-t ntfs3"
else
mount_args=
fi
2023-07-18 00:22:51 +08:00
# 挂载主分区
mkdir -p /os
2024-11-01 21:17:25 +08:00
mount $mount_args /dev/disk/by-label/os /os
2023-07-18 00:22:51 +08:00
# 挂载其他分区
if is_efi; then
2024-11-01 21:17:25 +08:00
mkdir -p /os/boot/efi
2023-07-18 00:22:51 +08:00
mount /dev/disk/by-label/efi /os/boot/efi
fi
mkdir -p /os/installer
mount $mount_args /dev/disk/by-label/installer /os/installer
2023-07-25 00:21:08 +08:00
}
2023-05-03 22:22:21 +08:00
get_dns_list_for_win() {
2024-08-19 00:33:59 +08:00
if dns_list=$(get_current_dns $1); then
i=0
for dns in $dns_list; do
i=$((i + 1))
echo "set ipv${1}_dns$i=$dns"
done
fi
}
create_win_set_netconf_script() {
target=$1
2024-09-06 23:00:36 +08:00
info "Create win netconf script"
2023-10-28 21:46:38 +08:00
if is_staticv4 || is_staticv6 || is_need_manual_set_dnsv6; then
get_netconf_to mac_addr
echo "set mac_addr=$mac_addr" >$target
# 生成静态 ipv4 配置
if is_staticv4; then
get_netconf_to ipv4_addr
get_netconf_to ipv4_gateway
cat <<EOF >>$target
set ipv4_addr=$ipv4_addr
set ipv4_gateway=$ipv4_gateway
2024-08-19 00:33:59 +08:00
$(get_dns_list_for_win 4)
EOF
fi
# 生成静态 ipv6 配置
if is_staticv6; then
get_netconf_to ipv6_addr
get_netconf_to ipv6_gateway
cat <<EOF >>$target
set ipv6_addr=$ipv6_addr
set ipv6_gateway=$ipv6_gateway
2023-10-28 21:46:38 +08:00
EOF
fi
# 有 ipv6 但需设置 dns 的情况
2024-08-19 00:33:59 +08:00
if is_need_manual_set_dnsv6; then
2023-10-28 21:46:38 +08:00
cat <<EOF >>$target
2024-08-19 00:33:59 +08:00
$(get_dns_list_for_win 6)
EOF
fi
cat -n $target
fi
# 脚本还有关闭ipv6隐私id的功能所以不能省略
# 合并脚本
wget $confhome/windows-set-netconf.bat -O- >>$target
unix2dos $target
}
2024-10-12 23:07:01 +08:00
create_win_change_rdp_port_script() {
target=$1
rdp_port=$2
info "Create win change rdp port script"
echo "set RdpPort=$rdp_port" >$target
wget $confhome/windows-change-rdp-port.bat -O- >>$target
unix2dos $target
}
# virt-what 要用最新版
# vultr 1G High Frequency LAX 实际上是 kvm
# debian 11 virt-what 1.19 显示为 hyperv qemu
# debian 11 systemd-detect-virt 显示为 microsoft
# alpine virt-what 1.25 显示为 kvm
# 所以不要在原系统上判断具体虚拟化环境
# lscpu 也可查看虚拟化环境,但 alpine on lightsail 运行结果为 Microsoft
# 猜测 lscpu 只参考了 cpuid 没参考 dmi
# virt-what 可能会输出多行结果,因此用 grep
get_aws_repo() {
if is_in_china >&2; then
echo https://s3.cn-north-1.amazonaws.com.cn/ec2-windows-drivers-downloads-cn
else
echo https://s3.amazonaws.com/ec2-windows-drivers-downloads
fi
}
get_client_name_by_build_ver() {
build_ver=$1
if [ "$build_ver" -ge 22000 ]; then
echo 11
elif [ "$build_ver" -ge 10240 ]; then
echo 10
elif [ "$build_ver" -ge 9600 ]; then
echo 8.1
elif [ "$build_ver" -ge 9200 ]; then
echo 8
elif [ "$build_ver" -ge 7600 ]; then
echo 7
elif [ "$build_ver" -ge 6000 ]; then
echo vista
else
error_and_exit "Unknown Build Version: $build_ver"
fi
}
# 将 AC/SAC 版本号 转换为 LTSC 版本号
# 用于查找驱动
get_server_name_by_build_ver() {
build_ver=$1
if [ "$build_ver" -ge 26100 ]; then
echo 2025
elif [ "$build_ver" -ge 20348 ]; then
echo 2022
elif [ "$build_ver" -ge 17763 ]; then
echo 2019
elif [ "$build_ver" -ge 14393 ]; then
echo 2016
elif [ "$build_ver" -ge 9600 ]; then
echo 2012 r2
elif [ "$build_ver" -ge 9200 ]; then
echo 2012
elif [ "$build_ver" -ge 7600 ]; then
echo 2008 r2
elif [ "$build_ver" -ge 6000 ]; then
echo 2008
else
error_and_exit "Unknown Build Version: $build_ver"
fi
}
is_nt_ver_ge() {
local orig sorted
orig=$(printf '%s\n' "$1" "$nt_ver")
sorted=$(echo "$orig" | sort -V)
[ "$orig" = "$sorted" ]
}
get_cloud_vendor() {
# busybox blkid 不显示 sr0 的 UUID
apk add lsblk
# http://git.annexia.org/?p=virt-what.git;a=blob;f=virt-what.in;hb=HEAD
# virt-what 可识别厂商 aws google_cloud alibaba_cloud alibaba_cloud-ebm
if is_dmi_contains "Amazon EC2" || is_virt_contains aws; then
echo aws
elif is_dmi_contains "Google Compute Engine" || is_dmi_contains "GoogleCloud" || is_virt_contains google_cloud; then
echo gcp
elif is_dmi_contains "OracleCloud"; then
echo oracle
elif is_dmi_contains "7783-7084-3265-9085-8269-3286-77"; then
echo azure
elif lsblk -o UUID,LABEL | grep -i 9796-932E | grep -iq config-2; then
echo ibm
elif is_dmi_contains 'Huawei Cloud'; then
echo huawei
elif is_dmi_contains 'Alibaba Cloud'; then
echo aliyun
fi
}
2024-11-01 21:17:25 +08:00
get_filesize_mb() {
du -m "$1" | awk '{print $1}'
}
2023-07-25 00:21:08 +08:00
install_windows() {
get_wim_prop() {
wim=$1
property=$2
wiminfo "$wim" | grep -i "^$property:" | cut -d: -f2- | xargs
}
get_image_prop() {
wim=$1
index=$2
property=$3
wiminfo "$wim" "$index" | grep -i "^$property:" | cut -d: -f2- | xargs
}
2024-09-06 23:00:36 +08:00
info "Process windows iso"
apk add wimlib
2024-01-22 19:00:17 +08:00
2023-05-25 20:15:12 +08:00
download $iso /os/windows.iso
mkdir -p /iso
2023-11-09 22:30:21 +08:00
mount -o ro /os/windows.iso /iso
2023-05-25 20:15:12 +08:00
# 防止用了不兼容架构的 iso
boot_index=$(get_wim_prop /iso/sources/boot.wim 'Boot Index')
arch_wim=$(get_image_prop /iso/sources/boot.wim "$boot_index" 'Architecture' | to_lower)
if ! {
{ [ "$(uname -m)" = "x86_64" ] && [ "$arch_wim" = x86_64 ]; } ||
{ [ "$(uname -m)" = "x86_64" ] && [ "$arch_wim" = x86 ]; } ||
{ [ "$(uname -m)" = "aarch64" ] && [ "$arch_wim" = arm64 ]; }
}; then
error_and_exit "The machine is $(uname -m), but the iso is $arch_wim."
fi
2024-11-01 21:17:25 +08:00
if [ -e /iso/sources/install.esd ]; then
iso_install_wim=/iso/sources/install.esd
install_wim=/os/installer/sources/install.esd
else
2024-11-01 21:17:25 +08:00
iso_install_wim=/iso/sources/install.wim
install_wim=/os/installer/sources/install.wim
fi
# 匹配映像版本
# 需要整行匹配,因为要区分 Windows 10 Pro 和 Windows 10 Pro for Workstations
2024-11-01 21:17:25 +08:00
image_count=$(wiminfo $iso_install_wim | grep "^Image Count:" | cut -d: -f2 | xargs)
all_image_names=$(wiminfo $iso_install_wim | grep ^Name: | sed 's/^Name: *//')
info "Images Count: $image_count"
echo "$all_image_names"
echo
if [ "$image_count" = 1 ]; then
# 只有一个版本就用那个版本
image_name=$all_image_names
else
while true; do
# 匹配成功
# 改成正确的大小写
if matched_image_name=$(echo "$all_image_names" | grep -ix "$image_name"); then
image_name=$matched_image_name
break
fi
# 匹配失败
2024-06-14 23:41:15 +08:00
file=/image-name
error "Invalid image name: $image_name"
2024-10-01 20:05:18 +08:00
echo "Choose a correct image name by one of follow command in ssh to continue:"
while read -r line; do
2024-06-14 23:41:15 +08:00
echo " echo '$line' >$file"
done < <(echo "$all_image_names")
# sleep 直到有输入
2024-06-14 23:41:15 +08:00
true >$file
while ! { [ -s $file ] && image_name=$(cat $file) && [ -n "$image_name" ]; }; do
sleep 1
done
done
fi
2024-01-22 19:00:17 +08:00
get_selected_image_prop() {
get_image_prop "$iso_install_wim" "$image_name" "$1"
}
# PRODUCTTYPE:
# - WinNT (普通 windows)
# - ServerNT (windows server)
# INSTALLATIONTYPE:
# - Client (普通 windows)
# - Server (windows server 带桌面体验)
# - Server Core (windows server 不带桌面体验)
2024-01-22 19:00:17 +08:00
# 用内核版本号筛选驱动
# 使得可以安装 Hyper-V Server / Azure Stack HCI 等 Windows Server 变种
nt_ver=$(get_selected_image_prop "Major Version").$(get_selected_image_prop "Minor Version")
build_ver=$(get_selected_image_prop "Build")
product_type=$(get_selected_image_prop "Product Type")
product_ver=$(
case $product_type in
WinNT) get_client_name_by_build_ver "$build_ver" ;;
ServerNT) get_server_name_by_build_ver "$build_ver" ;;
esac
)
2024-11-01 21:17:25 +08:00
info "Selected image info"
2024-09-06 23:00:36 +08:00
echo "Image Name: $image_name"
2024-11-01 21:17:25 +08:00
echo "Product Version: $product_ver"
echo "Product Type: $product_type"
2024-01-22 19:00:17 +08:00
echo "NT Version: $nt_ver"
2024-05-29 21:30:24 +08:00
echo "Build Version: $build_ver"
2024-11-01 21:17:25 +08:00
echo
# 复制 boot.wim 到 /os用于临时编辑
if [ -n "$boot_wim" ]; then
# 自定义 boot.wim 链接
download "$boot_wim" /os/boot.wim
else
cp /iso/sources/boot.wim /os/boot.wim
fi
# efi 启动目录为 efi 分区
# bios 启动目录为 os 分区
if is_efi; then
boot_dir=/os/boot/efi
else
boot_dir=/os
fi
# 复制启动相关的文件
# efi 额外复制efi目录
echo 'Copying boot files...'
cp -r /iso/boot* $boot_dir
if is_efi; then
echo 'Copying efi files...'
cp -r /iso/efi/ $boot_dir
fi
# 复制iso全部文件(除了boot.wim)到installer分区
echo 'Copying installer files...'
if false; then
rsync -rv \
--exclude=/sources/boot.wim \
--exclude=/sources/install.wim \
--exclude=/sources/install.esd \
2024-11-01 21:17:25 +08:00
/iso/* /os/installer/
else
(
cd /iso
find . -type f \
-not -name boot.wim \
-not -name install.wim \
-not -name install.esd \
2024-11-01 21:17:25 +08:00
-exec cp -r --parents {} /os/installer/ \;
)
fi
# 优化 install.wim
# 优点: 可以节省 200M~600M 空间,用来创建虚拟内存
# (意义不大,因为已经删除了 boot.wim 用来创建虚拟内存vista 除外)
2024-11-01 21:17:25 +08:00
# 缺点: 如果 install.wim 只有一个镜像,则只能缩小 10M+
2024-11-09 22:50:51 +08:00
if false; then
2024-11-01 21:17:25 +08:00
time wimexport --threads "$(get_build_threads 512)" "$iso_install_wim" "$image_name" "$install_wim"
info "install.wim size"
echo "Original: $(get_filesize_mb "$iso_install_wim")"
2024-11-01 21:17:25 +08:00
echo "Optimized: $(get_filesize_mb "$install_wim")"
echo
2024-11-01 21:17:25 +08:00
else
cp "$iso_install_wim" "$install_wim"
fi
# win11 要求 1GHz 2核1核超线程也行
# 用注册表无法绕过
# https://github.com/pbatard/rufus/issues/1990
2024-05-29 21:30:24 +08:00
# https://learn.microsoft.com/windows/iot/iot-enterprise/Hardware/System_Requirements
2024-10-01 20:05:18 +08:00
if [ "$product_ver" = "11" ] && [ "$(nproc)" -le 1 ]; then
wiminfo "$install_wim" "$image_name" --image-property WINDOWS/INSTALLATIONTYPE=Server
fi
2023-07-03 23:11:10 +08:00
# 变量名 使用场景
# arch_uname arch命令 / uname -m x86_64 aarch64
# arch_wim wiminfo x86 x86_64 ARM64
# arch virtio iso / unattend.xml x86 amd64 arm64
# arch_xdd virtio msi / xen驱动 x86 x64
# arch_dd 华为云驱动 32 64
2023-06-05 19:54:00 +08:00
# 将 wim 的 arch 转为驱动和应答文件的 arch
case "$arch_wim" in
2023-07-03 23:11:10 +08:00
x86)
arch=x86
arch_xdd=x86
arch_dd=32
2023-07-03 23:11:10 +08:00
;;
x86_64)
arch=amd64
arch_xdd=x64
arch_dd=64
2023-07-03 23:11:10 +08:00
;;
arm64)
arch=arm64
arch_xdd= # xen 没有 arm64 驱动,# virtio 也没有 arm64 msi
arch_dd= # 华为云没有 arm64 驱动
2023-07-03 23:11:10 +08:00
;;
2023-06-05 19:54:00 +08:00
esac
add_drivers() {
2024-09-06 23:00:36 +08:00
info "Add drivers"
drv=/os/drivers
mkdir -p "$drv" # 驱动下载临时文件夹
mkdir -p "/wim/drivers" # boot.wim 驱动文件夹
# 这里有坑
# $(get_cloud_vendor) 调用了 cache_dmi_and_virt
# 但是 $(get_cloud_vendor) 运行在 subshell 里面
# subshell 运行结束后里面的变量就消失了
# 因此先运行 cache_dmi_and_virt
cache_dmi_and_virt
vendor="$(get_cloud_vendor)"
# virtio
if is_virt_contains virtio; then
if [ "$vendor" = aliyun ] && is_nt_ver_ge 6.1 && [ "$arch_wim" = x86_64 ]; then
add_driver_aliyun_virtio
# 未测试是否需要专用驱动
elif false && [ "$vendor" = huawei ] && is_nt_ver_ge 6.0 && { [ "$arch_wim" = x86 ] || [ "$arch_wim" = x86_64 ]; }; then
add_driver_huawei_virtio
else
# 兜底
add_driver_generic_virtio
fi
fi
# xen
if is_virt_contains xen; then
# generic_xen 兜底,但未签名,暂停使用
if is_nt_ver_ge 6.1 && [ "$arch_wim" = x86_64 ]; then
add_driver_aws_xen
elif is_nt_ver_ge 6.0 && { [ "$arch_wim" = x86 ] || [ "$arch_wim" = x86_64 ]; }; then
add_driver_citrix_xen
fi
fi
2024-10-14 22:41:01 +08:00
# vmd
# 改进: 像检测 virtio 那样直接从 /sys 检测设备
# inf 有要求 19041 或以上
if [ "$build_ver" -ge 19041 ] && [ "$arch_wim" = x86_64 ] &&
is_lspci_contains 'Volume Management Device'; then
add_driver_vmd
fi
# 厂商驱动
case "$vendor" in
aws)
if is_nt_ver_ge 6.1 && { [ "$arch_wim" = x86_64 ] || [ "$arch_wim" = arm64 ]; }; then
add_driver_aws
fi
;;
azure)
# inf 不限版本,未测试
if [ "$arch_wim" = x86 ] || [ "$arch_wim" = x86_64 ]; then
add_driver_azure
fi
;;
gcp)
# inf 不限版本6.0 能装但用不了
# x86 x86_64 arm64 都有
add_driver_gcp
;;
esac
}
# aws nitro
# https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/aws-nvme-drivers.html
# https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/enhanced-networking-ena.html
add_driver_aws() {
2024-09-06 23:00:36 +08:00
info "Add drivers: AWS"
# 未打补丁的 win7 无法使用 sha256 签名的驱动
2024-01-28 22:46:38 +08:00
nvme_ver=$(
case "$nt_ver" in
6.1) echo 1.3.2 ;; # sha1 签名
6.2 | 6.3) echo 1.5.1 ;;
2024-01-28 22:46:38 +08:00
*) echo Latest ;;
esac
)
ena_ver=$(
case "$nt_ver" in
6.1) echo 2.1.4 ;; # sha1 签名
# 6.1) echo 2.2.3 ;; # sha256 签名
2024-01-28 22:46:38 +08:00
6.2 | 6.3) echo 2.6.0 ;;
*) echo Latest ;;
esac
)
[ "$arch_wim" = arm64 ] && arch_dir=/ARM64 || arch_dir=
download "$(get_aws_repo)/NVMe$arch_dir/$nvme_ver/AWSNVMe.zip" $drv/AWSNVMe.zip
download "$(get_aws_repo)/ENA$arch_dir/$ena_ver/AwsEnaNetworkDriver.zip" $drv/AwsEnaNetworkDriver.zip
2024-01-28 22:46:38 +08:00
2023-07-03 23:11:10 +08:00
unzip -o -d $drv/aws/ $drv/AWSNVMe.zip
unzip -o -d $drv/aws/ $drv/AwsEnaNetworkDriver.zip
cp_drivers $drv/aws
}
# citrix xen
add_driver_citrix_xen() {
2024-09-06 23:00:36 +08:00
info "Add drivers: Citrix Xen"
apk add 7zip
download https://s3.amazonaws.com/ec2-downloads-windows/Drivers/Citrix-Win_PV.zip $drv/Citrix-Win_PV.zip
unzip -o -d $drv $drv/Citrix-Win_PV.zip
case "$arch_wim" in
x86) override=s ;; # skip
x86_64) override=a ;; # always
esac
# 排除 $PLUGINSDIR $TEMP
exclude='$*'
2024-09-07 20:53:00 +08:00
7z x $drv/Citrix_xensetup.exe -o$drv/xen/ -ao$override -x!$exclude
cp_drivers $drv/xen
}
# aws xen
# https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/xen-drivers-overview.html
add_driver_aws_xen() {
2024-09-06 23:00:36 +08:00
info "Add drivers: AWS Xen"
2023-10-22 19:07:12 +08:00
apk add msitools
2023-07-06 22:20:09 +08:00
2024-01-28 22:46:38 +08:00
aws_pv_ver=$(
case "$nt_ver" in
6.1) echo 8.3.2 ;; # sha1 签名
# 6.1) echo 8.3.5 ;; # sha256 签名
6.2 | 6.3) echo 8.4.3 ;;
2024-01-28 22:46:38 +08:00
*) echo Latest ;;
esac
)
download "$(get_aws_repo)/AWSPV/$aws_pv_ver/AWSPVDriver.zip" $drv/AWSPVDriver.zip
2023-07-06 22:20:09 +08:00
2023-07-03 23:11:10 +08:00
unzip -o -d $drv $drv/AWSPVDriver.zip
msiextract $drv/AWSPVDriverSetup.msi -C $drv
mkdir -p $drv/aws/
cp -rf $drv/.Drivers/* $drv/aws/
cp_drivers $drv/xen -ipath "*/$arch_xdd/*"
}
# xen
# 没签名暂时用aws的驱动代替
# https://lore.kernel.org/xen-devel/E1qKMmq-00035B-SS@xenbits.xenproject.org/
# https://xenbits.xenproject.org/pvdrivers/win/
# 在 aws t2 上测试,安装 xenbus 会蓝屏装了其他7个驱动后能进系统但没网络
# 但 aws 应该用aws官方xen驱动所以测试仅供参考
add_driver_generic_xen() {
2024-09-06 23:00:36 +08:00
info "Add drivers: Generic Xen"
2023-07-03 23:11:10 +08:00
parts='xenbus xencons xenhid xeniface xennet xenvbd xenvif xenvkbd'
mkdir -p $drv/xen/
for part in $parts; do
2023-09-29 13:53:23 +08:00
download https://xenbits.xenproject.org/pvdrivers/win/$part.tar $drv/$part.tar
2023-07-03 23:11:10 +08:00
tar -xf $drv/$part.tar -C $drv/xen/
done
2024-09-07 20:53:00 +08:00
cp_drivers $drv/xen
}
2023-07-03 23:11:10 +08:00
# virtio
# https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/
add_driver_generic_virtio() {
info "Add drivers: Generic virtio"
2024-09-06 23:00:36 +08:00
2024-08-07 21:46:45 +08:00
# 要区分 win10 / win11 驱动,虽然他们的 NT 版本号都是 10.0,但驱动文件有区别
# https://github.com/virtio-win/kvm-guest-drivers-windows/commit/9af43da9e16e2d4bf4ea4663cdc4f29275fff48f
# vista >>> 2k8
# 10 >>> w10
# 2012 r2 >>> 2k12R2
virtio_sys=$(
case "$(echo "$product_ver" | to_lower)" in
'vista') echo 2k8 ;; # 没有 vista 文件夹
*)
case "$product_type" in
WinNT) echo "w$product_ver" ;;
ServerNT) echo "$product_ver" | sed -E -e 's/ //' -e 's/^200?/2k/' -e 's/r2/R2/' ;;
esac
;;
esac
)
2023-05-25 20:15:12 +08:00
2023-06-04 19:05:17 +08:00
# https://github.com/virtio-win/virtio-win-pkg-scripts/issues/40
# https://github.com/virtio-win/virtio-win-pkg-scripts/issues/61
case "$nt_ver" in
6.0 | 6.1) dir=archive-virtio/virtio-win-0.1.173-9 ;; # vista|w7|2k8|2k8R2
6.2 | 6.3) dir=archive-virtio/virtio-win-0.1.215-1 ;; # w8|w8.1|2k12|2k12R2
2023-06-04 19:05:17 +08:00
*) dir=stable-virtio ;;
esac
# vista|w7|2k8|2k8R2|arm64 要从 iso 获取驱动
if [ "$nt_ver" = 6.0 ] || [ "$nt_ver" = 6.1 ] || [ "$arch_wim" = arm64 ]; then
virtio_source=iso
else
virtio_source=msi
fi
2023-09-28 23:51:29 +08:00
baseurl=https://fedorapeople.org/groups/virt/virtio-win/direct-downloads
2023-09-28 23:51:29 +08:00
if [ "$virtio_source" = iso ]; then
download $baseurl/$dir/virtio-win.iso $drv/virtio.iso
mkdir -p $drv/virtio
mount -o ro $drv/virtio.iso $drv/virtio
if [ "$nt_ver" = 6.0 ] || [ "$nt_ver" = 6.1 ]; then
# vista/7 气球驱动有问题
cp_drivers $drv/virtio -ipath "*/$virtio_sys/$arch/*" -not -ipath "*/balloon/*"
else
cp_drivers $drv/virtio -ipath "*/$virtio_sys/$arch/*"
fi
else
# coreutils 的 cp mv rm 才有 -v 参数
apk add 7zip file coreutils
download $baseurl/$dir/virtio-win-gt-$arch_xdd.msi $drv/virtio.msi
match="FILE_*_${virtio_sys}_${arch}*"
7z x $drv/virtio.msi -o$drv/virtio -i!$match -y -bb1
# 为没有后缀名的文件添加后缀名
(
cd $drv/virtio
echo "Recognizing file extension..."
for file in *"${virtio_sys}_${arch}"; do
recognized=false
maybe_exts=$(file -b --extension "$file")
# exe/sys -> sys
# exe/com -> exe
# dll/cpl/tlb/ocx/acm/ax/ime -> dll
for ext in sys exe dll; do
if echo $maybe_exts | grep -qw $ext; then
recognized=true
mv -v "$file" "$file.$ext"
break
fi
done
# 如果识别不了后缀名,就删除此文件
# 因为用不了,免得占用空间
if ! $recognized; then
rm -fv "$file"
fi
done
# 将
# FILE_netkvm_netkvmco_w8.1_amd64.dll
# FILE_netkvm_w8.1_amd64.cat
# 改名为
# netkvmco.dll
# netkvm.cat
echo "Renaming files..."
for file in *; do
new_file=$(echo "$file" | sed "s|FILE_||; s|_${virtio_sys}_${arch}||; s|.*_||")
mv -v "$file" "$new_file"
done
)
# 虽然 vista/7 气球驱动有问题,但 msi 里面没有 vista/7 驱动
# 因此不用额外处理
cp_drivers $drv/virtio
2023-09-28 23:51:29 +08:00
fi
}
add_driver_huawei_virtio() {
info "Add drivers: Huawei virtio"
2024-09-06 23:00:36 +08:00
huawei_sys=$(
case "$(echo "$product_ver" | to_lower)" in
vista) echo Vista2008 ;;
7) echo 7 ;;
8) [ "$arch_wim" = x86 ] && echo 7 || echo 2012 ;; # 没有 win8 32/64
8.1) [ "$arch_wim" = x86 ] && echo 7 || echo 2012_R2 ;; # 没有 win8.1 32/64
10 | 11) echo 10 ;;
2008) echo Vista2008 ;;
'2008 r2') echo 2008_R2 ;;
2012) [ "$arch_wim" = x86 ] && echo 2008_R2 || echo 2012 ;; # 没有 2012 32
'2012 r2') echo 2012_R2 ;;
2016 | 2019 | 202*) echo 2016 ;;
esac
)
download https://ecs-instance-driver.obs.cn-north-1.myhuaweicloud.com/vmtools-windows.zip $drv/vmtools-windows.zip
unzip -o -d $drv $drv/vmtools-windows.zip
mkdir -p $drv/huawei
mount -o ro $drv/vmtools-windows.iso $drv/huawei
cp_drivers $drv/huawei -ipath "*/upgrade/windows ${huawei_sys}_${arch_dd}/drivers/*"
}
add_driver_aliyun_virtio() {
info "Add drivers: Aliyun virtio"
2024-09-06 23:00:36 +08:00
2024-09-09 00:19:11 +08:00
# win7 旧驱动是 sha1 签名
if [ "$nt_ver" = 6.1 ]; then
# 旧驱动
aliyun_sys=$(
case "$nt_ver" in
6.1) echo 7 ;;
6.2 | 6.3) echo 8 ;;
*) echo 10 ;;
esac
)
2024-09-09 00:19:11 +08:00
filename=$(
case "$nt_ver" in
6.1) echo 210408.1454.1459_bin.zip ;; # sha1
*) echo 220915.0953.0953_bin.zip ;; # sha256
# *) echo new_virtio.zip ;;
esac
)
2024-09-07 23:36:15 +08:00
2024-09-09 00:19:11 +08:00
region=$(
if is_in_china; then
echo cn-beijing
else
echo us-west-1
fi
)
download https://windows-driver-$region.oss-$region.aliyuncs.com/virtio/$filename $drv/aliyun.zip
unzip -o -d $drv/aliyun/ $drv/aliyun.zip
# 注意文件夹是 win7 Win8 win10 大小写不一致
cp_drivers $drv/aliyun -ipath "*/win${aliyun_sys}/${arch}/*"
else
# 新驱动
aliyun_sys=$(
case "$nt_ver" in
6.1) echo 2008R2 ;; # sha256
6.2 | 6.3) echo 2012R2 ;; # 实际上是 2012 的驱动
*) echo 2016 ;;
esac
)
2024-09-07 23:36:15 +08:00
2024-09-09 00:19:11 +08:00
region=cn-hangzhou
2024-09-09 00:19:11 +08:00
download https://windows-driver-$region.oss-$region.aliyuncs.com/virtio/AliyunVirtio_WIN$aliyun_sys.zip $drv/AliyunVirtio.zip
unzip -o -d $drv $drv/AliyunVirtio.zip
apk add innoextract
innoextract -d $drv/aliyun/ $drv/AliyunVirtio_*_WIN${aliyun_sys}_$arch_xdd.exe
apk del innoextract
cp_drivers $drv/aliyun -ipath "*/C$/Program Files/AliyunVirtio/*/drivers/*"
fi
}
# gcp
# x86 x86_64 arm64 都有
add_driver_gcp() {
2024-09-06 23:00:36 +08:00
info "Add drivers: GCP"
gce_repo=https://packages.cloud.google.com/yuck
download $gce_repo/repos/google-compute-engine-stable/index /tmp/gce.json
for name in gvnic gga; do
# gvnic 没有 arm64
if [ "$name" = gvnic ] && [ "$arch_wim" = arm64 ]; then
continue
fi
mkdir -p $drv/gce/$name
2024-05-22 21:49:24 +08:00
link=$(grep -o "/pool/.*-google-compute-engine-driver-$name.*\.goo" /tmp/gce.json)
wget $gce_repo$link -O- | tar -xzf- -C $drv/gce/$name
# 没有 win6.0 文件夹
# 但 inf 没限制
# TODO: 测试是否可用
if false; then
for suffix in '' '-32'; do
if [ -d "$drv/gce/$name/win6.1$suffix" ]; then
cp -r "$drv/gce/$name/win6.1$suffix" "$drv/gce/$name/win6.0$suffix"
fi
done
fi
case "$name" in
gvnic)
[ "$arch_wim" = x86 ] && suffix=-32 || suffix=
cp_drivers $drv/gce/gvnic -ipath "*/win$nt_ver$suffix/*"
;;
gga)
cp_drivers $drv/gce/gga -ipath "*/win$nt_ver/*"
;;
esac
done
}
# azure
# https://learn.microsoft.com/azure/virtual-network/accelerated-networking-mana-windows
add_driver_azure() {
2024-09-06 23:00:36 +08:00
info "Add drivers: Azure"
2024-01-28 22:46:38 +08:00
download https://aka.ms/manawindowsdrivers $drv/azure.zip
unzip $drv/azure.zip -d $drv/azure/
cp_drivers $drv/azure
}
2023-05-25 20:15:12 +08:00
2024-10-14 22:41:01 +08:00
add_driver_vmd() {
apk add 7zip
download https://downloadmirror.intel.com/820815/SetupRST.exe $drv/SetupRST.exe
7z x $drv/SetupRST.exe -o$drv/SetupRST -i!.text
7z x $drv/SetupRST/.text -o$drv/vmd
cp_drivers $drv/vmd
}
2023-05-25 20:15:12 +08:00
# 修改应答文件
2023-12-16 16:41:34 +08:00
download $confhome/windows.xml /tmp/autounattend.xml
locale=$(get_selected_image_prop 'Default Language')
2024-10-12 23:07:01 +08:00
use_default_rdp_port=$(is_need_change_rdp_port && echo false || echo true)
2024-10-13 22:58:12 +08:00
password_base64=$(get_password_windows_administrator_base64)
2024-10-12 23:07:01 +08:00
sed -i \
-e "s|%arch%|$arch|" \
-e "s|%image_name%|$image_name|" \
-e "s|%locale%|$locale|" \
2024-10-13 22:58:12 +08:00
-e "s|%administrator_password%|$password_base64|" \
2024-10-12 23:07:01 +08:00
-e "s|%use_default_rdp_port%|$use_default_rdp_port|" \
2024-05-22 21:56:11 +08:00
/tmp/autounattend.xml
2023-05-28 00:52:48 +08:00
# 修改应答文件,分区配置
2023-07-06 22:20:09 +08:00
if is_efi; then
2023-12-16 16:41:34 +08:00
sed -i "s|%installto_partitionid%|3|" /tmp/autounattend.xml
2023-05-28 00:52:48 +08:00
else
2023-12-16 16:41:34 +08:00
sed -i "s|%installto_partitionid%|1|" /tmp/autounattend.xml
fi
2023-06-04 19:09:40 +08:00
# vista/2008 有这行安装会报错
if [ "$nt_ver" = 6.0 ]; then
sed -i "/EnableFirewall/d" /tmp/autounattend.xml
fi
# 2012 r2删除 key 字段,报错 Windows cannot read the <ProductKey> setting from the unattend answer file即使创建 ei.cfg
# ltsc 2021有 ei.cfg填空白 key 正常
# ltsc 2021 n有 ei.cfg填空白 key 报错 Windows Cannot find Microsoft software license terms
# 评估版 iso ei.cfg 有 EVAL 字样,填空白 key 报错 Windows Cannot find Microsoft software license terms
# key
if [[ "$image_name" = 'Windows Vista'* ]]; then
2024-01-28 22:46:38 +08:00
# vista 需密钥,密钥可与 edition 不一致
# TODO: 改成从网页获取?
# https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys
key=VKK3X-68KWM-X2YGT-QR4M6-4BWMV
sed -i "s/%key%/$key/" /tmp/autounattend.xml
else
# shellcheck disable=SC2010
if ls -d /os/installer/sources/* | grep -iq ei.cfg; then
# 镜像有 ei.cfg删除 key 字段
sed -i "/%key%/d" /tmp/autounattend.xml
2024-01-28 22:46:38 +08:00
else
# 镜像无 ei.cfg填空白 key
sed -i "s/%key%//" /tmp/autounattend.xml
fi
fi
2023-05-28 00:52:48 +08:00
# 挂载 boot.wim
2024-09-06 23:00:36 +08:00
info "mount boot.wim"
mkdir -p /wim
wimmountrw /os/boot.wim "$boot_index" /wim/
2023-05-25 20:15:12 +08:00
2023-07-03 23:11:10 +08:00
cp_drivers() {
src=$1
shift
2023-09-28 23:51:29 +08:00
2023-07-03 23:11:10 +08:00
find $src \
-type f \
2023-07-03 23:11:10 +08:00
-not -iname "*.pdb" \
-not -iname "dpinst.exe" \
"$@" \
2023-09-28 23:51:29 +08:00
-exec cp -rfv {} /wim/drivers \;
2023-07-03 23:11:10 +08:00
}
# 添加驱动
add_drivers
2023-05-25 20:15:12 +08:00
# win7 要添加 bootx64.efi 到 efi 目录
2023-06-05 19:54:00 +08:00
[ $arch = amd64 ] && boot_efi=bootx64.efi || boot_efi=bootaa64.efi
2023-07-06 22:20:09 +08:00
if is_efi && [ ! -e /os/boot/efi/efi/boot/$boot_efi ]; then
2023-05-25 20:15:12 +08:00
mkdir -p /os/boot/efi/efi/boot/
cp /wim/Windows/Boot/EFI/bootmgfw.efi /os/boot/efi/efi/boot/$boot_efi
fi
2023-05-28 00:52:48 +08:00
# 复制应答文件
2023-12-16 16:41:34 +08:00
# 移除注释,否则 windows-setup.bat 重新生成的 autounattend.xml 有问题
2023-11-15 20:51:44 +08:00
apk add xmlstarlet
2023-12-16 16:41:34 +08:00
xmlstarlet ed -d '//comment()' /tmp/autounattend.xml >/wim/autounattend.xml
unix2dos /wim/autounattend.xml
2024-09-06 23:00:36 +08:00
info "autounattend.xml"
# 查看最终文件,并屏蔽密码
xmlstarlet ed -d '//*[name()="AdministratorPassword" or name()="Password"]' /wim/autounattend.xml | cat -n
apk del xmlstarlet
2023-11-15 20:51:44 +08:00
# 避免无参数运行 setup.exe 时自动安装
mv /wim/autounattend.xml /wim/windows.xml
2023-11-15 20:51:44 +08:00
# 复制安装脚本
# https://slightlyovercomplicated.com/2016/11/07/windows-pe-startup-sequence-explained/
mv /wim/setup.exe /wim/setup.exe.disabled
# 如果有重复的 Windows/System32 文件夹,会提示找不到 winload.exe 无法引导
# win7 win10 是 Windows/System32
# win2016 是 windows/system32
2024-01-27 23:17:50 +08:00
# shellcheck disable=SC2010
system32_dir=$(ls -d /wim/*/*32 | grep -i windows/system32)
download $confhome/windows-setup.bat $system32_dir/startnet.cmd
2023-05-28 00:52:48 +08:00
# 提交修改 boot.wim
2024-09-06 23:00:36 +08:00
info "Unmount boot.wim"
2023-05-28 00:52:48 +08:00
wimunmount --commit /wim/
2024-11-01 21:17:25 +08:00
# 原地优化可以用以下命令之一
# wimdelete /os/boot.wim 1
# wimoptimize /os/boot.wim
# 优化 boot.wim 并复制到正确的位置
mkdir -p $boot_dir/sources/
if is_nt_ver_ge 6.1; then
# win7 或以上删除 boot.wim 镜像 1 不会报错
# 因为 win7 winre 镜像在 install.wim Windows\System32\Recovery\winRE.wim
2024-11-01 21:17:25 +08:00
images=$boot_index
else
# vista 删除 boot.wim 镜像 1 会报错
# Windows cannot access the required file Drive:\Sources\Boot.wim.
# Make sure all files required for installation are available and restart the installation.
# Error code: 0x80070491
# vista install.wim 没有 Windows\System32\Recovery\winRE.wim
2024-11-01 21:17:25 +08:00
images=all
fi
2024-11-01 21:17:25 +08:00
wimexport --boot /os/boot.wim "$images" $boot_dir/sources/boot.wim
info "boot.wim size"
echo "Original: $(get_filesize_mb /iso/sources/boot.wim)"
2024-11-01 21:17:25 +08:00
echo "Added Drivers: $(get_filesize_mb /os/boot.wim)"
echo "Optimized: $(get_filesize_mb "$boot_dir/sources/boot.wim")"
echo
# vista 安装时需要 boot.wim原因见上面
if [ "$nt_ver" = 6.0 ] &&
! [ -e /os/installer/sources/boot.wim ]; then
2024-11-01 21:17:25 +08:00
cp $boot_dir/sources/boot.wim /os/installer/sources/boot.wim
fi
2023-05-28 00:52:48 +08:00
# windows 7 没有 invoke-webrequest
# installer分区盘符不一定是D盘
# 所以复制 resize.bat 到 install.wim
if true; then
2024-09-06 23:00:36 +08:00
info "mount install.wim"
2023-07-06 21:52:48 +08:00
wimmountrw $install_wim "$image_name" /wim/
if false; then
2023-12-16 16:41:34 +08:00
# 使用 autounattend.xml
# win7 在此阶段找不到网卡
download $confhome/windows-resize.bat /wim/windows-resize.bat
for ethx in $(get_eths); do
create_win_set_netconf_script /wim/windows-set-netconf-$ethx.bat
done
else
modify_windows /wim
fi
2024-09-06 23:00:36 +08:00
info "Unmount install.wim"
2023-07-06 21:52:48 +08:00
wimunmount --commit /wim/
fi
2023-05-25 20:15:12 +08:00
2023-05-28 00:52:48 +08:00
# 添加引导
2023-07-06 22:20:09 +08:00
if is_efi; then
2024-10-12 23:07:01 +08:00
# 现在 add_default_efi_to_nvram() 添加 bootx64.efi 到最前面
# 因此这里重复了
if false; then
apk add efibootmgr
efibootmgr -c -L "Windows Installer" -d /dev/$xda -p1 -l "\\EFI\\boot\\$boot_efi"
fi
2023-05-28 00:52:48 +08:00
else
# 或者用 ms-sys
apk add grub-bios
2024-07-09 23:37:51 +08:00
# efi 下,强制安装 mbr 引导,需要添加 --target i386-pc
grub-install --target i386-pc --boot-directory=/os/boot /dev/$xda
2023-05-28 00:52:48 +08:00
cat <<EOF >/os/boot/grub/grub.cfg
set timeout=5
menuentry "reinstall" {
2024-11-01 21:17:25 +08:00
search --no-floppy --label --set=root os
2023-05-28 00:52:48 +08:00
ntldr /bootmgr
}
EOF
fi
2023-07-25 00:21:08 +08:00
}
2023-05-25 20:15:12 +08:00
# 添加 netboot.efi 备用
download_netboot_xyz_efi() {
dir=$1
2024-09-06 23:00:36 +08:00
info "download netboot.xyz.efi"
file=$dir/netboot.xyz.efi
if [ "$(uname -m)" = aarch64 ]; then
download https://boot.netboot.xyz/ipxe/netboot.xyz-arm64.efi $file
else
download https://boot.netboot.xyz/ipxe/netboot.xyz.efi $file
fi
}
2024-02-01 00:59:34 +08:00
refind_main_disk() {
2024-03-01 22:01:22 +08:00
if true; then
apk add sfdisk
main_disk=$(sfdisk --disk-id /dev/$xda | sed 's/0x//')
else
apk add lsblk
# main_disk=$(blkid --match-tag PTUUID -o value /dev/$xda)
2024-03-01 22:01:22 +08:00
main_disk=$(lsblk --nodeps -rno PTUUID /dev/$xda)
fi
2024-02-01 00:59:34 +08:00
}
sync_time() {
if false; then
# arm要手动从硬件同步时间避免访问https出错
# do 机器第二次运行会报错
hwclock -s || true
fi
# ntp 时间差太多会无法同步?
# http 时间可能不准确,毕竟不是专门的时间服务器
# 也有可能没有 date header?
method=http
case "$method" in
ntp)
if is_in_china; then
ntp_server=ntp.aliyun.com
else
ntp_server=pool.ntp.org
fi
# -d[d] Verbose
# -n Run in foreground
# -q Quit after clock is set
# -p PEER
ntpd -d -n -q -p "$ntp_server"
;;
http)
url=$(grep -m1 ^http /etc/apk/repositories)
# 可能有多行,取第一行
date_header=$(wget -S --no-check-certificate --spider "$url" 2>&1 | grep -m1 '^ Date:')
# gnu date 不支持 -D
busybox date -u -D " Date: %a, %d %b %Y %H:%M:%S GMT" -s "$date_header"
;;
esac
hwclock -w
}
2024-04-27 09:45:10 +08:00
get_ubuntu_kernel_flavor() {
# 20.04/22.04 kvm 内核 vnc 没显示
# 24.04 kvm = virtual
# linux-image-virtual = linux-image-6.x-generic
# linux-image-generic = linux-image-6.x-generic + amd64-microcode + intel-microcode + linux-firmware + linux-modules-extra-generic
2024-09-06 23:00:33 +08:00
# TODO: ISO virtual-hwe-24.04 不安装 linux-image-extra-virtual-hwe-24.04 不然会花屏
2024-04-27 09:45:10 +08:00
# https://github.com/systemd/systemd/blob/main/src/basic/virt.c
# https://github.com/canonical/cloud-init/blob/main/tools/ds-identify
# http://git.annexia.org/?p=virt-what.git;a=blob;f=virt-what.in;hb=HEAD
2024-09-06 23:00:33 +08:00
if [ "$releasever" = 16.04 ]; then
if is_virt; then
echo virtual-hwe-$releasever
2024-04-27 09:45:10 +08:00
else
2024-09-06 23:00:33 +08:00
echo generic-hwe-$releasever
fi
else
2024-10-18 23:58:04 +08:00
# 这里有坑
# $(get_cloud_vendor) 调用了 cache_dmi_and_virt
# 但是 $(get_cloud_vendor) 运行在 subshell 里面
# subshell 运行结束后里面的变量就消失了
# 因此先运行 cache_dmi_and_virt
cache_dmi_and_virt
2024-09-06 23:00:33 +08:00
vendor="$(get_cloud_vendor)"
case "$vendor" in
aws | gcp | oracle | azure | ibm) echo $vendor ;;
*)
if is_virt; then
echo virtual-hwe-$releasever
else
2024-09-06 23:00:33 +08:00
echo generic-hwe-$releasever
fi
2024-09-06 23:00:33 +08:00
;;
esac
fi
2024-04-27 09:45:10 +08:00
}
2023-07-25 00:21:08 +08:00
install_redhat_ubuntu() {
2024-09-06 23:00:36 +08:00
info "Download iso installer"
2023-07-25 00:21:08 +08:00
# 安装 grub2
if is_efi; then
# 注意低版本的grub无法启动f38 arm的内核
# https://forums.fedoraforum.org/showthread.php?330104-aarch64-pxeboot-vmlinuz-file-format-changed-broke-PXE-installs
apk add grub-efi efibootmgr
grub-install --efi-directory=/os/boot/efi --boot-directory=/os/boot
2023-05-13 00:14:46 +08:00
else
2023-07-25 00:21:08 +08:00
apk add grub-bios
grub-install --boot-directory=/os/boot /dev/$xda
2023-05-13 00:14:46 +08:00
fi
2023-05-03 22:22:21 +08:00
2023-07-25 00:21:08 +08:00
# 重新整理 extra因为grub会处理掉引号要重新添加引号
2024-02-01 00:59:34 +08:00
extra_cmdline=''
for var in $(grep -o '\bextra_[^ ]*' /proc/cmdline | xargs); do
if [[ "$var" = "extra_main_disk="* ]]; then
2024-02-01 00:59:34 +08:00
# 重新记录主硬盘
refind_main_disk
extra_cmdline="$extra_cmdline extra_main_disk=$main_disk"
2024-02-01 00:59:34 +08:00
else
extra_cmdline="$extra_cmdline $(echo $var | sed -E "s/(extra_[^=]*)=(.*)/\1='\2'/")"
2024-02-01 00:59:34 +08:00
fi
2023-07-25 00:21:08 +08:00
done
2023-05-03 22:22:21 +08:00
2023-09-10 22:23:03 +08:00
# 安装红帽系时,只有最后一个有安装界面显示
# https://anaconda-installer.readthedocs.io/en/latest/boot-options.html#console
console_cmdline=$(get_ttys console=)
2023-07-25 00:21:08 +08:00
grub_cfg=/os/boot/grub/grub.cfg
2023-05-03 22:22:21 +08:00
2023-07-25 00:21:08 +08:00
# 新版grub不区分linux/linuxefi
# shellcheck disable=SC2154
if [ "$distro" = "ubuntu" ]; then
download $iso /os/installer/ubuntu.iso
2024-09-06 23:00:33 +08:00
mkdir -p /iso
mount -o ro /os/installer/ubuntu.iso /iso
2023-05-03 22:22:21 +08:00
2024-09-06 23:00:33 +08:00
# 内核风味
2024-04-27 09:45:10 +08:00
kernel=$(get_ubuntu_kernel_flavor)
2023-08-28 19:33:53 +08:00
2024-09-06 23:00:33 +08:00
# 要安装的版本
# https://canonical-subiquity.readthedocs-hosted.com/en/latest/reference/autoinstall-reference.html#id
# 20.04 不能选择 minimal ,也没有 install-sources.yaml
source_id=
if [ -f /iso/casper/install-sources.yaml ]; then
ids=$(grep id: /iso/casper/install-sources.yaml | awk '{print $2}')
if [ "$(echo "$ids" | wc -l)" = 1 ]; then
source_id=$ids
else
[ "$minimal" = 1 ] && v= || v=-v
source_id=$(echo "$ids" | grep $v '\-minimal')
if [ "$(echo "$source_id" | wc -l)" -gt 1 ]; then
error_and_exit "find multi source id."
fi
fi
fi
2023-07-25 00:21:08 +08:00
# 正常写法应该是 ds="nocloud-net;s=https://xxx/" 但是甲骨文云的ds更优先自己的ds根本无访问记录
# $seed 是 https://xxx/
cat <<EOF >$grub_cfg
2023-05-03 22:22:21 +08:00
set timeout=5
menuentry "reinstall" {
# https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/1851311
2023-05-28 00:52:48 +08:00
# rmmod tpm
2023-11-09 22:30:21 +08:00
insmod all_video
2023-05-03 22:22:21 +08:00
search --no-floppy --label --set=root installer
loopback loop /ubuntu.iso
2024-09-06 23:00:33 +08:00
linux (loop)/casper/vmlinuz iso-scan/filename=/ubuntu.iso autoinstall noprompt noeject cloud-config-url=$ks $extra_cmdline extra_kernel=$kernel extra_source_id=$source_id --- $console_cmdline
2023-05-03 22:22:21 +08:00
initrd (loop)/casper/initrd
}
EOF
2023-07-25 00:21:08 +08:00
else
download $vmlinuz /os/vmlinuz
download $initrd /os/initrd.img
download $squashfs /os/installer/install.img
2023-05-03 22:22:21 +08:00
2023-07-25 00:21:08 +08:00
cat <<EOF >$grub_cfg
2023-05-03 22:22:21 +08:00
set timeout=5
menuentry "reinstall" {
2023-11-09 22:30:21 +08:00
insmod all_video
2023-05-03 22:22:21 +08:00
search --no-floppy --label --set=root os
2023-09-10 22:23:03 +08:00
linux /vmlinuz inst.stage2=hd:LABEL=installer:/install.img inst.ks=$ks $extra_cmdline $console_cmdline
2023-05-13 00:14:46 +08:00
initrd /initrd.img
2023-05-03 22:22:21 +08:00
}
EOF
2023-07-25 00:21:08 +08:00
fi
2024-02-01 01:02:35 +08:00
cat "$grub_cfg"
2023-07-25 00:21:08 +08:00
}
2024-08-20 00:27:51 +08:00
trans() {
2024-09-06 23:00:36 +08:00
info "start trans"
2024-08-20 00:27:51 +08:00
mod_motd
# 先检查 modloop 是否正常
# 防止格式化硬盘后,缺少 ext4 模块导致 mount 失败
# https://github.com/bin456789/reinstall/issues/136
ensure_service_started modloop
2024-08-20 00:27:51 +08:00
cat /proc/cmdline
clear_previous
add_community_repo
# 需要在重新分区之前,找到主硬盘
# 重新运行脚本时,可指定 xda
# xda=sda ash trans.start
if [ -z "$xda" ]; then
find_xda
fi
if [ "$distro" != "alpine" ]; then
setup_web_if_enough_ram
# util-linux 包含 lsblk
# util-linux 可自动探测 mount 格式
apk add util-linux
2024-08-20 00:27:51 +08:00
fi
# dd qemu 切换成云镜像模式,暂时没用到
2024-09-06 23:00:32 +08:00
# shellcheck disable=SC2154
2024-08-20 00:27:51 +08:00
if [ "$distro" = "dd" ] && [ "$img_type" = "qemu" ]; then
# 移到 reinstall.sh ?
distro=any
cloud_image=1
fi
if is_use_cloud_image; then
case "$img_type" in
qemu)
create_part
download_qcow
case "$distro" in
2024-11-26 22:09:05 +08:00
centos | almalinux | rocky | oracle | redhat | anolis | opencloudos | openeuler)
2024-08-20 00:27:51 +08:00
# 这几个系统云镜像系统盘是8~9g xfs而我们的目标是能在5g硬盘上运行因此改成复制系统文件
install_qcow_by_copy
;;
ubuntu)
# 24.04 云镜像有 boot 分区(在系统分区之前),因此不直接 dd 云镜像
install_qcow_by_copy
;;
*)
# debian fedora opensuse arch gentoo any
dd_qcow
resize_after_install_cloud_image
modify_os_on_disk linux
;;
esac
;;
2024-09-06 23:00:32 +08:00
raw)
# 暂时没用到 raw 格式的云镜像
dd_raw_with_extract
2024-08-20 00:27:51 +08:00
resize_after_install_cloud_image
modify_os_on_disk linux
;;
esac
elif [ "$distro" = "dd" ]; then
case "$img_type" in
2024-09-06 23:00:32 +08:00
raw)
dd_raw_with_extract
2024-10-18 23:58:03 +08:00
if false; then
# linux 扩容后无法轻易缩小,例如 xfs
# windows 扩容在 windows 下完成
resize_after_install_cloud_image
fi
2024-08-20 00:27:51 +08:00
modify_os_on_disk windows
;;
qemu) # dd qemu 不可能到这里,因为上面已处理
;;
esac
else
# 安装模式
case "$distro" in
alpine)
install_alpine
;;
arch | gentoo)
create_part
install_arch_gentoo
;;
nixos)
create_part
install_nixos
;;
*)
create_part
mount_part_for_iso_installer
case "$distro" in
2024-11-26 22:09:05 +08:00
centos | almalinux | rocky | fedora | ubuntu | redhat) install_redhat_ubuntu ;;
2024-08-20 00:27:51 +08:00
windows) install_windows ;;
esac
;;
esac
fi
# 需要用到 lsblk efibootmgr ,只要 1M 左右容量
# 因此 alpine 不单独处理
if is_efi; then
del_invalid_efi_entry
2024-09-06 23:00:32 +08:00
add_default_efi_to_nvram
2024-08-20 00:27:51 +08:00
fi
2024-09-06 23:00:36 +08:00
info 'done'
2024-08-20 00:27:51 +08:00
# 让 web 输出全部内容
sleep 5
}
2023-07-25 00:21:08 +08:00
# 脚本入口
2024-03-17 00:08:48 +08:00
# debian initrd 会寻找 main
# 并调用本文件的 create_ifupdown_config 方法
: main
2024-08-20 00:27:51 +08:00
# 复制脚本
2024-08-19 00:33:59 +08:00
# 用于打印错误或者再次运行
2024-08-20 00:27:51 +08:00
# 路径相同则不用复制
# 重点:要在删除脚本之前复制
2024-08-19 00:33:59 +08:00
if ! [ "$(readlink -f "$0")" = /trans.sh ]; then
cp -f "$0" /trans.sh
fi
2024-08-20 00:27:51 +08:00
trap 'trap_err $LINENO $?' ERR
2024-08-19 00:33:59 +08:00
2024-08-20 00:27:51 +08:00
# 删除本脚本,不然会被复制到新系统
2024-08-19 00:33:59 +08:00
rm -f /etc/local.d/trans.start
rm -f /etc/runlevels/default/local
2024-08-20 00:27:51 +08:00
# 提取变量
2024-08-19 00:33:59 +08:00
extract_env_from_cmdline
2024-08-20 00:27:51 +08:00
# 带参数运行部分
# 重新下载并 exec 运行新脚本
2024-08-19 00:33:59 +08:00
if [ "$1" = "update" ]; then
2024-09-06 23:00:36 +08:00
info 'update script'
2024-08-19 00:33:59 +08:00
# shellcheck disable=SC2154
wget -O /trans.sh "$confhome/trans.sh"
chmod +x /trans.sh
exec /trans.sh
elif [ "$1" = "alpine" ]; then
info 'switch to alpine'
distro=alpine
# 后面的步骤很多都会用到这个,例如分区布局
cloud_image=0
2024-08-19 00:33:59 +08:00
fi
2024-08-20 00:27:51 +08:00
# 无参数运行部分
2024-03-31 00:47:13 +08:00
# 允许 ramdisk 使用所有内存,默认是 50%
mount / -o remount,size=100%
# 同步时间
# 1. 可以防止访问 https 出错
# 2. 可以防止 https://github.com/bin456789/reinstall/issues/223
# E: Release file for http://security.ubuntu.com/ubuntu/dists/noble-security/InRelease is not valid yet (invalid for another 5h 37min 18s).
# Updates for this repository will not be applied.
# 3. 不能直接读取 rtc因为默认情况 windows rtc 是本地时间linux rtc 是 utc 时间
# 4. 允许同步失败,因为不是关键步骤
sync_time || true
2023-07-25 00:21:08 +08:00
# 设置密码,安装并打开 ssh
2024-10-13 22:58:12 +08:00
echo "root:$(get_password_linux_sha512)" | chpasswd -e
2024-10-12 23:07:01 +08:00
apk add openssh
if is_need_change_ssh_port; then
change_ssh_port / $ssh_port
fi
2023-07-25 00:21:08 +08:00
printf '\nyes' | setup-sshd
2024-08-20 00:27:51 +08:00
# shellcheck disable=SC2154
if [ "$hold" = 1 ]; then
if is_run_from_locald; then
2024-09-06 23:00:36 +08:00
info "hold"
2024-08-20 00:27:51 +08:00
exit
fi
2023-07-25 00:21:08 +08:00
fi
2024-08-20 00:27:51 +08:00
# 正式运行重装
# shellcheck disable=SC2046,SC2194
case 1 in
1)
# ChatGPT 说这种性能最高
2024-10-18 23:34:54 +08:00
exec > >(exec tee $(get_ttys /dev/) /reinstall.log) 2>&1
2024-08-20 00:27:51 +08:00
trans
;;
2)
2024-10-18 23:34:54 +08:00
exec > >(tee $(get_ttys /dev/) /reinstall.log) 2>&1
2024-08-20 00:27:51 +08:00
trans
;;
3)
2024-10-18 23:34:54 +08:00
trans 2>&1 | tee $(get_ttys /dev/) /reinstall.log
2024-08-20 00:27:51 +08:00
;;
esac
2023-10-31 22:49:49 +08:00
if [ "$hold" = 2 ]; then
2024-09-06 23:00:36 +08:00
info "hold 2"
2023-07-25 00:21:08 +08:00
exit
2023-05-03 22:22:21 +08:00
fi
2023-10-28 21:46:38 +08:00
2024-08-20 00:27:51 +08:00
# swapoff -a
# umount ?
sync
2023-05-03 22:22:21 +08:00
reboot