root / gen-alpine.sh
Created at Tue Oct 02 06:10:15 GMT 2018
shell script to help install alpine to x86 pc
gen-alpine.sh
Raw
#!/bin/sh
#genetate alpine edge x86/x86_64 image: chmod +x gen-alpine.sh && sudo ./gen-alpine.sh
#depends: apk-tools-static, vim(xxd)

set -x

die() {
	printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2  # bold red
	exit 1
}

if [ "$(id -u)" -ne 0 ]; then
	die 'This script must be run as root!'
fi

which xxd >/dev/null || exit 

BUILD_DATE="$(date +%Y-%m-%d)"

usage() {
	cat <<EOF
	Usage: gen-alpine.sh [options]
	Valid options are:
		-a ARCH                 Options: x86, x86_64.
		-m ALPINE_MIRROR        URI of the mirror to fetch packages from
		                        (default is https://mirrors.tuna.tsinghua.edu.cn/alpine).
		-o OUTPUT_IMG           Output img file
		                        (default is BUILD_DATE-alpine-ARCH.img).
		-h                      Show this help message and exit.
EOF
}

while getopts 'a:m:o:h' OPTION; do
	case "$OPTION" in
		a) ARCH="$OPTARG";;
		m) ALPINE_MIRROR="$OPTARG";;
		o) OUTPUT="$OPTARG";;
		h) usage; exit 0;;
	esac
done 

: ${ARCH:="$(uname -m)"}
: ${ALPINE_MIRROR:="https://mirrors.tuna.tsinghua.edu.cn/alpine"}
: ${OUTPUT_IMG:="${BUILD_DATE}-alpine-${ARCH}.img"}

case $ARCH in
	 x86| i[3456]86 ) ARCH=x86 GRUB_EFI_TARGET=i386-efi;; 
	   x64 | x86_64 ) ARCH=X86_64 GRUB_EFI_TARGET=x86_64-efi;; 
	               *) die 'not supported arch';;
esac

#=======================  F u n c t i o n s  =======================#

gen_image() {
	fallocate -l $(( 700 * 1024 *1024 )) "$OUTPUT_IMG"
cat > fdisk.cmd <<-EOF
	o
	n
	p
	1
	
	+100MB
	t
	c
	a
	n
	p
	2
	
	
	w
EOF
fdisk "$OUTPUT_IMG" < fdisk.cmd
rm -f fdisk.cmd
}

do_format() {
	mkfs.fat -F32 "$BOOT_DEV"
	mkfs.ext4 "$ROOT_DEV"
	mkdir -p mnt
	mount "$ROOT_DEV" mnt
	mkdir -p mnt/boot
	mount "$BOOT_DEV" mnt/boot
}

setup_mirrors() {
	mv mnt/etc/apk/repositories mnt/etc/apk/repositories.old

	for ALPINE_REPOS in main community testing ; do
		echo ${ALPINE_MIRROR}/edge/${ALPINE_REPOS} >> mnt/etc/apk/repositories
	done
}

do_apkstrap() {
	apk.static -X ${ALPINE_MIRROR}/edge/main -U --allow-untrusted --root mnt --initdb add alpine-base
}

install_kernel() {
	apk add linux-vanilla
}

gen_grub_cfg() {
	cat > /boot/grub/grub.cfg <<- EOF
	set timeout=2
	insmod all_video
	menuentry "Alpine Linux" {
	    search --no-floppy --fs-uuid --set=root ${BOOT_UUID}
	    echo	'Loading Linux vanilla ...'
	    linux   /vmlinuz-vanilla root=UUID=${ROOT_UUID} rw  modules=sd-mod,usb-storage,ext4 nomodeset quiet rootfstype=ext4
	    echo	'Loading initial ramdisk ...'
	    initrd  /initramfs-vanilla
	}
	EOF
}	

install_bootloader() {

	# grub-bios

	apk add grub grub-bios
	grub-install --target=i386-pc --recheck --boot-directory /boot ${LOOP_DEV}
	
	# grub-efi
	apk add grub-efi efibootmgr
	grub-install --target=${GRUB_EFI_TARGET} --efi-directory=/boot --boot-directory=/boot --bootloader-id=grub --removable

	gen_grub_cfg

	# sometimes after reboot, the bootloader work not normal, seems need to reboot again
	# alpine x86 efi bootloader not support x64 pc
}

gen_fstabs() {
	echo "PARTUUID=${BOOT_PARTUUID}  /boot           vfat    defaults          0       2
PARTUUID=${ROOT_PARTUUID}  /               ext4    defaults,noatime         0       1"
}

gen_resize2fs_once_service() {
	cat > /etc/init.d/resize2fs-once <<'EOF'
#!/sbin/openrc-run
command="/usr/bin/resize2fs-once"
command_background=false
depend() {
        after modules
        need localmount
}
EOF

	cat > /usr/bin/resize2fs-once <<'EOF'
#!/bin/sh 
set -xe
ROOT_DEV=$(findmnt / -o source -n)
cat > /tmp/fdisk.cmd <<-EOF
	d
	2
	
	n
	p
	2
	
	
	w
	EOF
fdisk "$(echo "$ROOT_DEV" | sed -E 's/p?2$//')" < /tmp/fdisk.cmd
rm -f /tmp/fdisk.cmd
partprobe
resize2fs "$ROOT_DEV"
rc-update del resize2fs-once default
#reboot
EOF

chmod +x /etc/init.d/resize2fs-once /usr/bin/resize2fs-once
rc-update add resize2fs-once default
}

gen_nm_ntpd_dispatcher_scripts() {
        cat > /etc/NetworkManager/dispatcher.d/10-ntpd <<'EOF'
#!/bin/sh

set -xe

case "$2" in
        up)
                rc-service ntpd restart
        ;;
        down)
                rc-service ntpd stop
        ;;
esac
EOF

chmod +x /etc/NetworkManager/dispatcher.d/10-ntpd
}

make_bash_fancy() {
	su alpine <<-'EOF'
	sh -c 'cat > /home/alpine/.profile << "EOF"
	if [ -f "$HOME/.bashrc" ] ; then
	    source $HOME/.bashrc
	fi
	EOF'
	
	wget https://gist.github.com/yangxuan8282/f2537770982a5dec74095ce4f32de59c/raw/ce003332eff55d50738b726f68a1b493c6867594/.bashrc -P /home/alpine
	EOF
}

add_normal_user() {
	addgroup alpine
	adduser -G alpine -s /bin/bash -D alpine
	echo "alpine:alpine" | /usr/sbin/chpasswd
	echo "alpine ALL=NOPASSWD: ALL" >> /etc/sudoers
}

add_user_groups() {
	for USER_GROUP in adm dialout cdrom audio users wheel video games plugdev input netdev; do
		adduser alpine $USER_GROUP
	done
}

setup_ntp_server() {
	sed -i 's/pool.ntp.org/cn.pool.ntp.org/' /etc/init.d/ntpd
}

install_xorg_driver() {
	apk add xorg-server xf86-video-intel xf86-video-fbdev xf86-input-libinput
}

install_xfce4() {

	install_xorg_driver

	apk add xfce4 xfce4-mixer xfce4-wavelan-plugin lxdm paper-icon-theme arc-theme \
		gvfs gvfs-smb sshfs \
        	network-manager-applet gnome-keyring

	mkdir -p /usr/share/wallpapers &&
	curl https://img2.goodfon.com/original/2048x1820/3/b6/android-5-0-lollipop-material-5355.jpg \
		--output /usr/share/wallpapers/android-5-0-lollipop-material-5355.jpg

	su alpine sh -c 'mkdir -p /home/alpine/.config && \
	wget https://github.com/yangxuan8282/dotfiles/archive/master.tar.gz -O- | \
		tar -C /home/alpine/.config -xzf - --strip=2 dotfiles-master/alpine-config'

	sed -i 's/^# autologin=dgod/autologin=alpine/' /etc/lxdm/lxdm.conf
	sed -i 's|^# session=/usr/bin/startlxde|session=/usr/bin/startxfce4|' /etc/lxdm/lxdm.conf

	rc-update add lxdm default
}

# take from postmarketOS

setup_openrc_service() {
	setup-udev -n

	for service in devfs dmesg; do
		rc-update add $service sysinit
	done
	for service in hwclock modules sysctl hostname bootmisc swclock syslog; do
		rc-update add $service boot
	done
	for service in dbus haveged sshd wpa_supplicant ntpd local networkmanager; do
		rc-update add $service default
	done
	for service in mount-ro killprocs savecache; do
		rc-update add $service shutdown
	done

	mkdir -p /run/openrc
	touch /run/openrc/shutdowntime
}

gen_nm_config() {
	cat <<EOF
[main]
plugins+=ifupdown
dhcp=dhcpcd

[ifupdown]
managed=true

[logging]
level=INFO

[device-mac-randomization]
wifi.scan-rand-mac-address=yes
EOF
}

gen_wpa_supplicant_config() {
	sed -i 's/wpa_supplicant_args=\"/wpa_supplicant_args=\" -u -Dwext,nl80211/' /etc/conf.d/wpa_supplicant
	touch /etc/wpa_supplicant/wpa_supplicant.conf
}

gen_syslog_config() {
	sed s/=\"/=\""-C4048 "/  -i /etc/conf.d/syslog
}

gen_env() {
	echo "LOOP_DEV=${LOOP_DEV}
	ROOT_PARTUUID=${ROOT_PARTUUID}
	BOOT_UUID=${BOOT_UUID}
	ROOT_UUID=${ROOT_UUID}
	GRUB_EFI_TARGET=${GRUB_EFI_TARGET}"
}

setup_chroot() {
	chroot mnt /bin/sh <<-EOF
		set -xe
		source /root/env_file
		source /root/functions
		rm -f /root/functions /root/env_file
		echo "root:toor" | chpasswd
		apk --update add sudo
		add_normal_user
		echo "alpine" > /etc/hostname
		echo "127.0.0.1    alpine alpine.localdomain" > /etc/hosts
		apk add dbus eudev haveged openssh util-linux shadow e2fsprogs e2fsprogs-extra tzdata
		apk add iw wireless-tools crda wpa_supplicant networkmanager
		apk add nano htop bash bash-completion curl tar
		apk add ca-certificates wget && update-ca-certificates
		setup_openrc_service
		add_user_groups
		gen_nm_config > /etc/NetworkManager/conf.d/networkmanager.conf
		gen_nm_ntpd_dispatcher_scripts
		gen_wpa_supplicant_config
		gen_syslog_config
		setup_ntp_server
		ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
		make_bash_fancy
		gen_resize2fs_once_service		
		install_bootloader
		install_kernel
		#install_xfce4
EOF
}

mounts() {
	mount -t proc none mnt/proc
	mount -o bind /sys mnt/sys
	mount -o bind /dev mnt/dev
}

umounts() {
	umount mnt/dev
	umount mnt/sys
	umount mnt/proc
	umount mnt/boot
	umount mnt
	losetup -d "$LOOP_DEV"
}

#=======================  F u n c t i o n s  =======================#

pass_function() {
	sed -nE '/^#===.*F u n c t i o n s.*===#/,/^#===.*F u n c t i o n s.*===#/p' "$0"
}

gen_image

LOOP_DEV=$(losetup --partscan --show --find "${OUTPUT_IMG}")
BOOT_DEV="$LOOP_DEV"p1
ROOT_DEV="$LOOP_DEV"p2

do_format

do_apkstrap

IMGID="$(dd if="${OUTPUT_IMG}" skip=440 bs=1 count=4 2>/dev/null | xxd -e | cut -f 2 -d' ')"

BOOT_PARTUUID="${IMGID}-01"
ROOT_PARTUUID="${IMGID}-02"

BOOT_UUID=$(blkid ${BOOT_DEV} | cut -f 2 -d '"')
ROOT_UUID=$(blkid ${ROOT_DEV} | cut -f 2 -d '"')

gen_fstabs > mnt/etc/fstab

gen_env > mnt/root/env_file

pass_function > mnt/root/functions

mounts

setup_mirrors

cp /etc/resolv.conf mnt/etc/resolv.conf

setup_chroot

umounts

cat >&2 <<-EOF
	---
	Installation is complete
	Flash to usb disk with: dd if=${OUTPUT_IMG} of=/dev/TARGET_DEV bs=4M
	
EOF
gen-alpine_rpi.sh
Raw
#!/bin/sh
# shell script to help install alpine to raspberry pi, support armhf/aarch64

set -xe

die() {
	printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2  # bold red
	exit 1
}

if [ "$(id -u)" -ne 0 ]; then
	die 'This script must be run as root!'
fi

which wget > /dev/null || die 'please install wget first'

cd "$(dirname "$0")"

DEST=$(pwd)

branch="edge"
chroot_dir="$DEST/mnt"

usage() {
	cat <<EOF
	Usage: gen-alpine.sh [options]
	Valid options are:
		-a ARCH                 Options: armhf, aarch64.
		-m ALPINE_MIRROR        URI of the mirror to fetch packages from
		                        (default is https://mirrors.ustc.edu.cn/alpine).
		-h                      Show this help message and exit.
EOF
}

while getopts 'a:m:h' OPTION; do
	case "$OPTION" in
		a) ALPINE_ARCH="$OPTARG";;
		m) ALPINE_MIRROR="$OPTARG";;
		h) usage; exit 0;;
	esac
done 

: ${ALPINE_ARCH:="$(uname -m)"}
: ${ALPINE_MIRROR:="https://mirrors.ustc.edu.cn/alpine"}


case ${ALPINE_ARCH} in
	armhf | armv[4-9]*) ALPINE_ARCH="armhf";;
	aarch64 | arm64) ALPINE_ARCH="aarch64";;
	*) die 'not supported arch';;
esac

mkdir -p ${chroot_dir}

wget ${ALPINE_MIRROR}/${branch}/main/${ALPINE_ARCH}/apk-tools-static-2.10.0_rc1-r0.apk

tar -xzf apk-tools-static-*.apk

./sbin/apk.static -X ${ALPINE_MIRROR}/${branch}/main -U --allow-untrusted --root ${chroot_dir} --initdb add alpine-base

cp /etc/resolv.conf ${chroot_dir}/etc/
mkdir -p ${chroot_dir}/root

mkdir -p ${chroot_dir}/etc/apk

for repositories in main community testing ; do
	echo "${ALPINE_MIRROR}/${branch}/${repositories}" >> ${chroot_dir}/etc/apk/repositories
done

#=======================  F u n c t i o n s  =======================#

mount_devices() {
	mount -t proc none ${chroot_dir}/proc
	mount -o bind /sys ${chroot_dir}/sys
	mount -o bind /dev ${chroot_dir}/dev
}

umount_devices() {
	umount ${chroot_dir}/proc
	umount ${chroot_dir}/sys
	umount ${chroot_dir}/dev
}

gen_fstabs() {
	echo "/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1"
}

add_normal_user() {
	addgroup pi
	adduser -G pi -s /bin/bash -D pi
	echo "pi:raspberry" | /usr/sbin/chpasswd
	echo "pi ALL=NOPASSWD: ALL" >> /etc/sudoers
}

add_user_groups() {
	for USER_GROUP in spi i2c gpio; do
		groupadd -f -r $USER_GROUP
	done
	for USER_GROUP in adm dialout cdrom audio users wheel video games plugdev input gpio spi i2c netdev; do
		adduser pi $USER_GROUP
	done
}

gen_config_txt_32b() {
        cat > /boot/config.txt <<EOF
disable_splash=0
boot_delay=0
gpu_mem=256
gpu_mem_256=64
[pi0]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi1]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi2]
kernel=vmlinuz-rpi2
initramfs initramfs-rpi2
[pi3]
kernel=vmlinuz-rpi2
initramfs initramfs-rpi2
[all]
include usercfg.txt
EOF
}

gen_config_txt_64b() {
        cat > /boot/config.txt <<EOF
disable_splash=0
boot_delay=0
gpu_mem=256
gpu_mem_256=64

# 64bit-mode
arm_control=0x200

kernel=vmlinuz-rpi
initramfs initramfs-rpi

device_tree_address=0x100
device_tree_end=0x8000

include usercfg.txt
EOF
}

gen_config_txt() {
	case $ALPINE_ARCH in
		armhf) gen_config_txt_32b;;
		aarch64) gen_config_txt_64b;;
	esac
}

gen_usercfg_txt() {
	cat > /boot/usercfg.txt <<EOF
enable_uart=1

dtparam=sd_overclock=100
EOF
}

get_rpi_blobs() {
# for branch other than edge, since bootloader package not exist, need to download it directly

	for blobs in bootcode.bin fixup.dat start.elf ; do
		wget -P /boot https://github.com/raspberrypi/firmware/raw/master/boot/${blobs}
	done
#	apk add raspberrypi-bootloader
}

get_rpi_firmware() {
	# since alpine upstream package linux-firmware-brcm have include those firmwares, no need to download it now

	for wifi_fw in 43455-sdio.bin 43455-sdio.clm_blob 43455-sdio.txt ; do
		wget -P /lib/firmware/brcm https://github.com/RPi-Distro/firmware-nonfree/raw/master/brcm/brcmfmac${wifi_fw} 
	done

	for bt_fw in BCM43430A1.hcd BCM4345C0.hcd ; do
		wget -P /lib/firmware/brcm https://github.com/RPi-Distro/bluez-firmware/raw/master/broadcom/${bt_fw}
	done
}

gen_cmdline_txt() {
	echo "root=/dev/mmcblk0p2 modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1" > /boot/cmdline.txt
}

setup_ntp_server() {
	sed -i 's/pool.ntp.org/cn.pool.ntp.org/' /etc/chrony/chrony.conf
}

add_vchiq_udev_rules() {
	mkdir -p /etc/udev/rules.d

	cat > /etc/udev/rules.d/raspberrypi.rules <<"EOF"
SUBSYSTEM=="vchiq|input", MODE="0777"
KERNEL=="mouse*|mice|event*",  MODE="0777"
EOF
}

gen_restart_chronyd_once_service() {
	cat > /etc/init.d/restart-chronyd-once <<'EOF'
#!/sbin/openrc-run

command="/usr/bin/restart-chronyd-once"
command_background=false

depend() {
        after local
        need networkmanager
}
EOF

	cat > /usr/bin/restart-chronyd-once <<'EOF'
#!/bin/sh
# seems need to restart chronyd once after first boot

set -xe

rc-service chronyd restart

rc-update del restart-chronyd-once default
EOF

	chmod +x /usr/bin/restart-chronyd-once /etc/init.d/restart-chronyd-once
	rc-update add restart-chronyd-once default
}

# take from postmarketOS

setup_openrc_service() {
	setup-udev -n

	for service in devfs dmesg; do
		rc-update add $service sysinit
	done
	for service in modules sysctl hostname bootmisc swclock syslog; do
		rc-update add $service boot
	done
	for service in dbus haveged sshd wpa_supplicant chronyd local networkmanager; do
		rc-update add $service default
	done
	for service in mount-ro killprocs savecache; do
		rc-update add $service shutdown
	done

	mkdir -p /run/openrc
	touch /run/openrc/shutdowntime
}

install_kernel() {
	case $ALPINE_ARCH in
		armhf) KERNEL_FLAVOR="linux-rpi linux-rpi2";;
		aarch64) KERNEL_FLAVOR=linux-rpi;;
	esac

	apk add $KERNEL_FLAVOR

	cd /usr/lib/linux-*/
	find . -type f -regex ".*\.dtbo\?$" -exec install -Dm644 {} /boot/{} \;
}

gen_nm_config() {
	cat > /etc/NetworkManager/conf.d/networkmanager.conf <<EOF
[main]
plugins+=ifupdown
dhcp=dhcpcd

[ifupdown]
managed=true

[logging]
level=INFO

[device-mac-randomization]
wifi.scan-rand-mac-address=yes
EOF
}

gen_wpa_supplicant_config() {
	sed -i 's/wpa_supplicant_args=\"/wpa_supplicant_args=\" -u -Dwext,nl80211/' /etc/conf.d/wpa_supplicant
	touch /etc/wpa_supplicant/wpa_supplicant.conf
}

gen_syslog_config() {
	sed s/=\"/=\""-C4048 "/  -i /etc/conf.d/syslog
}

gen_env() {
	echo "ALPINE_ARCH=${ALPINE_ARCH}"
}

gen_rpi_alpine_image() {
	chroot ${chroot_dir} /bin/sh <<-EOF
		set -xe
		source /root/env_file
		source /etc/profile
		source /root/functions
		rm -f /root/functions
		echo "root:toor" | chpasswd
		apk --update add sudo
		add_normal_user
		echo "raspberrypi" > /etc/hostname
		echo "127.0.0.1    raspberrypi raspberrypi.localdomain" > /etc/hosts
		apk add dbus eudev haveged chrony openssh util-linux shadow e2fsprogs tzdata
		apk add iw wireless-tools crda wpa_supplicant networkmanager
		apk add nano htop bash bash-completion curl
		apk add ca-certificates wget && update-ca-certificates
		setup_openrc_service
		add_user_groups
		gen_nm_config
		gen_wpa_supplicant_config
		echo "options cfg80211 ieee80211_regdom=CN" > /etc/modprobe.d/cfg80211.conf
		gen_syslog_config
		setup_ntp_server
		ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
		install_kernel
		get_rpi_blobs
		#get_rpi_firmware
		add_vchiq_udev_rules
		gen_restart_chronyd_once_service
		#apk add omxplayer
		gen_cmdline_txt
		gen_config_txt
		gen_usercfg_txt
		gen_fstabs > /etc/fstab
EOF
}

#=======================  F u n c t i o n s  =======================#

pass_function() {
	sed -nE '/^#===.*F u n c t i o n s.*===#/,/^#===.*F u n c t i o n s.*===#/p' "$0"
}

mount_devices

gen_env > mnt/root/env_file

pass_function > ${chroot_dir}/root/functions

gen_rpi_alpine_image

umount_devices
wifi-helper.md
Raw
cat > /boot/wifi_config <<"EOF"
WIFI_SSID=
WIFI_PSK=
EOF
cat > /usr/bin/auto-connectwifi <<"EOF"
#!/bin/sh
#dummy script to connect wifi on headless rpi

set -xe

if [ -f /boot/wifi_config ]; then
    source /boot/wifi_config &&
    rm -f /boot/wifi_config &&
    nmcli dev wifi connect ${WIFI_SSID} password ${WIFI_PSK}
fi
EOF
cat > /etc/init.d/nm-wifi-helper <<"EOF"
#!/sbin/openrc-run

command="/usr/bin/auto-connectwifi"
command_background=false

depend() {
    after local
    need networkmanager wpa_supplicant
}
EOF
chmod +x /etc/init.d/nm-wifi-helper /usr/bin/auto-connectwifi
rc-update add nm-wifi-helper default