summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO15
-rw-r--r--.gitignore8
-rw-r--r--90-dosync-virtio-no-rename.link7
-rw-r--r--Makefile30
-rw-r--r--PKGBUILD30
-rw-r--r--README.md79
-rw-r--r--digitalocean-synchronize.install4
-rw-r--r--digitalocean-synchronize.service3
-rw-r--r--digitalocean-synchronize.sh (renamed from digitalocean-synchronize)6
-rwxr-xr-xinstall.sh802
10 files changed, 47 insertions, 937 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 46ced9520c9a..1de46a756517 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,15 +1,18 @@
pkgbase = digitalocean-synchronize
pkgdesc = DigitalOcean Synchronization (passwords, keys, networks)
- pkgver = 2.4
- pkgrel = 2
+ pkgver = 2.6
+ pkgrel = 1
url = https://github.com/gh2o/digitalocean-debian-to-arch
- install = digitalocean-synchronize.install
arch = any
license = GPL
- source = digitalocean-synchronize
+ depends = wget
+ options = !strip
+ source = digitalocean-synchronize.sh
source = digitalocean-synchronize.service
- sha256sums = 2115bcf34d80186103e4399f5a20d410145ee50d316a67bdfe6f43c4b11d2064
- sha256sums = 5888d367a08604b17528d58aa26050209d8ececf7ed35f90b5e96b31165b6a1c
+ source = 90-dosync-virtio-no-rename.link
+ sha256sums = 37261e4f5a79a5308e8e94925a037cc2e3d13fa5a473f6fc9b57bed07c06ed5d
+ sha256sums = 0e51944270c52293f81ea63cb73af42f93341009ddf714ca3a7afe9d4d15a2a8
+ sha256sums = d85cde96e602a4ff296d18a7769c683a66feffe5db35a03cdeab651922681f85
pkgname = digitalocean-synchronize
diff --git a/.gitignore b/.gitignore
index c57f3cc85950..41bbaa7d4257 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
.swp
.*.swp
-*pkg.tar*
-/install.pkg.sh
-*.src.tar.gz
+
+# generated by makepkg
+/pkg/
+/src/
+*.pkg.tar.*
diff --git a/90-dosync-virtio-no-rename.link b/90-dosync-virtio-no-rename.link
new file mode 100644
index 000000000000..5b432b60af71
--- /dev/null
+++ b/90-dosync-virtio-no-rename.link
@@ -0,0 +1,7 @@
+# Prevent virtio network devices from being renamed.
+
+[Match]
+Driver=virtio_net
+
+[Link]
+NamePolicy=kernel
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 30089ce1572f..000000000000
--- a/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-PKGNAME = $(shell grep ^pkgname PKGBUILD | sed -e 's:.*=::')
-PKGVER = $(shell grep ^pkgver PKGBUILD | sed -e 's:.*=::')
-PKGREL = $(shell grep ^pkgrel PKGBUILD | sed -e 's:.*=::')
-PKGARCH = $(shell grep ^arch PKGBUILD | sed -e 's:.*=::')
-
-PKG = $(PKGNAME)-$(PKGVER)-$(PKGREL)-$(PKGARCH).pkg.tar.xz
-PKG_SRC = $(PKGNAME)-$(PKGVER)-$(PKGREL).src.tar.gz
-
-DEPS = digitalocean-synchronize \
- digitalocean-synchronize.service \
- digitalocean-synchronize.install \
- PKGBUILD \
- Makefile
-
-install.pkg.sh: $(PKG)
- @cat $(subst .pkg,,$@) > $@
- @echo -e '\ncat <<EMBEDDED\n\n!!!!digitalocean-synchronize.pkg.tar.xz' >> $@
- @base64 $< >> $@
- @echo -e '!!!!\n\nEMBEDDED' >> $@
-
- $(info Build complete!)
- $(info Output file: $@)
-
-$(PKG): $(DEPS)
- updpkgsums
- mksrcinfo
- makepkg -fc
-
-clean:
- rm -f $(PKG) install.pkg.sh
diff --git a/PKGBUILD b/PKGBUILD
index f650732122b3..aeb35efbe970 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,21 +2,31 @@
# Contributor: Kyle Manna <kyle at kylemanna dot com>
pkgname=digitalocean-synchronize
-pkgver=2.4
-pkgrel=2
+pkgver=2.6
+pkgrel=1
pkgdesc='DigitalOcean Synchronization (passwords, keys, networks)'
url='https://github.com/gh2o/digitalocean-debian-to-arch'
-arch=any
-license=GPL
-install=digitalocean-synchronize.install
-source=('digitalocean-synchronize'
- 'digitalocean-synchronize.service')
+arch=(any)
+license=(GPL)
+options=(!strip)
-sha256sums=('2115bcf34d80186103e4399f5a20d410145ee50d316a67bdfe6f43c4b11d2064'
- '5888d367a08604b17528d58aa26050209d8ececf7ed35f90b5e96b31165b6a1c')
+depends=(wget)
+
+source=(digitalocean-synchronize.sh
+ digitalocean-synchronize.service
+ 90-dosync-virtio-no-rename.link)
+
+sha256sums=('37261e4f5a79a5308e8e94925a037cc2e3d13fa5a473f6fc9b57bed07c06ed5d'
+ '0e51944270c52293f81ea63cb73af42f93341009ddf714ca3a7afe9d4d15a2a8'
+ 'd85cde96e602a4ff296d18a7769c683a66feffe5db35a03cdeab651922681f85')
package() {
- install -Dm755 digitalocean-synchronize ${pkgdir}/usr/bin/digitalocean-synchronize
+ install -Dm755 digitalocean-synchronize.sh ${pkgdir}/usr/bin/digitalocean-synchronize
install -Dm644 digitalocean-synchronize.service ${pkgdir}/usr/lib/systemd/system/digitalocean-synchronize.service
+ install -Dm644 90-dosync-virtio-no-rename.link ${pkgdir}/usr/lib/systemd/network/90-dosync-virtio-no-rename.link
+
+ local wantsdir=${pkgdir}/usr/lib/systemd/system/multi-user.target.wants
+ install -dm755 ${wantsdir}
+ ln -s ../digitalocean-synchronize.service ${wantsdir}/
}
diff --git a/README.md b/README.md
deleted file mode 100644
index 3b3172054a47..000000000000
--- a/README.md
+++ /dev/null
@@ -1,79 +0,0 @@
-DigitalOcean Debian to Arch
-===========================
-DigitalOcean deprecated Arch Linux a while back because it was relatively
-difficult to support due to the rolling updates. I wrote this script to
-bring it back! This script downloads a bootstrap Arch Linux image, updates it
-to the latest version, then overwrites the host operating system with it.
-Unlike Debian 7.x, Debian 8.x on DigitalOcean boots traditionally (through the
-MBR and Grub), so no dirty *kexec* magic is needed.
-
-Warning / Disclaimer
---------------------
-<h3>ALL DATA ON THE DROPLET WILL BE UNCONDITIONALLY DESTROYED.</h3>
-This script may cause your VPS to become unbootable.
-I only recommend running this script on newly created droplets with no
-important data.
-
-Installation
-------------
-1. Create a new Debian 8.x droplet (either 32-bit or 64-bit is fine).
-2. In the droplet, run the following as root:
- `wget https://raw.githubusercontent.com/gh2o/digitalocean-debian-to-arch/debian8/install.sh && bash install.sh`
-3. Follow the instructions when prompted.
-4. Sit back and relax! The system will automatically reboot once complete,
- and you should have a fully updated Arch Linux system in within minutes.
-
-Advanced Configuration
-----------------------
-This script supports several flags, all of which are optional.
-
-* `--archlinux_mirror`
- The Arch Linux mirror from which the bootstrap image and packages should be
- downloaded. Defaults to the DigitalOcean mirror at
- http://mirrors.digitalocean.com/archlinux.
-* `--kernel_package`
- The kernel package to install. Defaults to the vanilla `linux` package.
- Other options include `linux-lts` for long term support and `linux-grsec` for
- a kernel with grsecurity/PaX patches.
-* `--target_architecture`
- The architecture of the new Arch Linux installation. Defaults to the
- architecture of the original Debian image as provided by `uname -m`.
- A 64-bit Debian image may convert to either `x86_64` or `i686`.
- A 32-bit Debian image may only convert to `i686`.
-* `--target_disklabel`
- The type of partition table to use. Defaults to `gpt` (GUID partition table
- as used by EFI). The alternative is `dos` (traditional MBR).
-* `--target_filesystem`
- The filesystem on which the Arch Linux installation should be installed.
- Defaults to `ext4`. The alternative is `btrfs`.
-
-How it Works
-------------
-1. A sparse disk image is created with the same size of the droplet's disk.
-2. Three partitions are made and formatted.
- * **DORoot**: A "dummy" partition to keep DigitalOcean happy. When snapshots
- are restored, new passwords are written here.
- * **BIOSBoot**: The virtual machine BIOS cannot boot from GPT partitions
- directly, so a small partition is placed here for bootloader code.
- * **ArchRoot**: The main root filesystem for Arch Linux.
-3. The Arch Linux bootstrap image is downloaded and unpacked onto ArchRoot.
-4. `pacman -Syu` is called inside the image to pull in all the base packages
- along with OpenSSH.
-5. The root password and SSH host keys are copied into the image.
-6. A special script called `digitalocean-synchronize` is installed into
- the image. This script is run at every startup to autodetect the network
- settings from the metadata service. It also detects if the droplet
- was just restored, and if so, it resets the root password and regenerates
- the host SSH keys.
-7. The image is now ready. The script then generates a "blockplan". It is
- essentially a list of instructions to image the virtual disk with the
- sparse disk image without requiring any extra space.
-8. A minimal root filesystem is generated on RAM so that the disk can
- be unmounted.
-9. The script calls `systemctl switch-root` to enter the minimal
- root filesystem.
-10. The disk is unmounted.
-11. The blockplan is executed.
-12. The bootloader (Grub) is installed.
-13. Reboot!
-14. Done!
diff --git a/digitalocean-synchronize.install b/digitalocean-synchronize.install
deleted file mode 100644
index ca8c7b497d9f..000000000000
--- a/digitalocean-synchronize.install
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-post_install() {
- systemctl enable digitalocean-synchronize.service
-}
diff --git a/digitalocean-synchronize.service b/digitalocean-synchronize.service
index 74a0c1cd3596..045b8d3a9fa9 100644
--- a/digitalocean-synchronize.service
+++ b/digitalocean-synchronize.service
@@ -7,6 +7,3 @@ After=systemd-udevd.service
[Service]
Type=oneshot
ExecStart=/usr/sbin/digitalocean-synchronize
-
-[Install]
-WantedBy=multi-user.target
diff --git a/digitalocean-synchronize b/digitalocean-synchronize.sh
index 35593fa1900f..bf6827baceb5 100644
--- a/digitalocean-synchronize
+++ b/digitalocean-synchronize.sh
@@ -83,6 +83,12 @@ process_interface() {
fi
log "Added IPv4 address ${address}/${prefix} on ${interface}."
fi
+ if [[ " ${attrs} " =~ " anchor_ipv4/ " ]]; then
+ local address=$(curl -sf ${url}anchor_ipv4/address)
+ local prefix=$(netmask_to_prefix $(curl -sf ${url}anchor_ipv4/netmask))
+ echo "Address=${address}/${prefix}"
+ log "Added Anchor IPv4 address ${address}/${prefix} on ${interface}."
+ fi
if [[ " ${attrs} " =~ " ipv6/ " ]]; then
local address=$(curl -sf ${url}ipv6/address)
local prefix=$(curl -sf ${url}ipv6/cidr)
diff --git a/install.sh b/install.sh
deleted file mode 100755
index e4b4c3fb1843..000000000000
--- a/install.sh
+++ /dev/null
@@ -1,802 +0,0 @@
-#!/bin/bash
-
-################################################################################
-### INSTRUCTIONS AT https://github.com/gh2o/digitalocean-debian-to-arch/ ###
-################################################################################
-
-run_from_file() {
- local f t
- for f in /dev/fd/*; do
- [ -h $f ] || continue
- [ $f -ef "$0" ] && return
- done
- t=$(mktemp)
- cat > $t
- if [ "$(head -n 1 $t)" = '#!/bin/bash' ]; then
- chmod +x $t
- exec /bin/bash $t "$@" </dev/fd/2
- else
- rm -f $t
- echo "Direct execution not supported with this shell ($_)." >&2
- echo "Please try bash instead." >&2
- exit 1
- fi
-}
-
-# do not modify the two lines below
-[ -h /dev/fd/0 ] && run_from_file
-#!/bin/bash
-
-########################################
-### DEFAULT CONFIGURATION ###
-########################################
-
-# mirror from which to download archlinux packages
-archlinux_mirror="http://mirrors.digitalocean.com/archlinux"
-
-# package to use as kernel (linux or linux-lts)
-kernel_package=linux
-
-# migrated machine architecture (x86_64/i686)
-target_architecture="$(uname -m)"
-
-# new disklabel type (gpt/dos)
-target_disklabel="gpt"
-
-# new filesystem type (ext4/btrfs)
-target_filesystem="ext4"
-
-# NOT EXPOSED NORMALLY: don't prompt
-continue_without_prompting=0
-
-########################################
-### END OF CONFIGURATION ###
-########################################
-
-if [ -n "${POSIXLY_CORRECT}" ] || [ -z "${DEBIAN_TO_ARCH_ENV_CLEARED}" ]; then
- exec /usr/bin/env -i \
- TERM="$TERM" \
- PATH=/usr/sbin:/sbin:/usr/bin:/bin \
- DEBIAN_TO_ARCH_ENV_CLEARED=1 \
- /bin/bash "$0" "$@"
-fi
-
-set -eu
-set -o pipefail
-shopt -s nullglob
-shopt -s dotglob
-umask 022
-
-sector_size=512
-
-flag_variables=(
- archlinux_mirror
- kernel_package
- target_architecture
- target_disklabel
- target_filesystem
-)
-
-host_packages=(
- haveged
- parted
-)
-
-arch_packages=(
- grub
- openssh
-)
-
-gpt1_size_MiB=1
-doroot_size_MiB=6
-biosboot_size_MiB=1
-archroot_size_MiB=
-gpt2_size_MiB=1
-
-doroot_offset_MiB=$((gpt1_size_MiB))
-biosboot_offset_MiB=$((doroot_offset_MiB + doroot_size_MiB))
-archroot_offset_MiB=$((biosboot_offset_MiB + biosboot_size_MiB))
-
-log() {
- echo "[$(date)]" "$@" >&2
-}
-
-fatal() {
- log "$@"
- log "Exiting."
- exit 1
-}
-
-extract_embedded_file() {
- awk -v n="$1" '$0=="!!!!"{p=0};p;$0=="!!!!"n{p=1}' "$0" | base64 --decode
-}
-
-parse_flags() {
- local c conf_key conf_val
- while [ $# -gt 0 ]; do
- conf_key=
- conf_val=
- for c in ${flag_variables[@]}; do
- case "$1" in
- --$c)
- shift
- [ $# -gt 0 ] || fatal "Option $c requires a value."
- conf_key="$c"
- conf_val="$1"
- shift
- break
- ;;
- --$c=*)
- conf_key="$c"
- conf_val="${1#*=}"
- shift
- break
- ;;
- --i_understand_that_this_droplet_will_be_completely_wiped)
- continue_without_prompting=1
- conf_key=option_acknowledged
- shift
- break
- ;;
- --help)
- print_help_and_exit
- ;;
- esac
- done
- [ "${conf_key}" = option_acknowledged ] && continue
- [ -n "${conf_key}" ] || fatal "Unknown option: $1"
- [ -n "${conf_val}" ] || fatal "Empty value for option ${conf_key}."
- local -n conf_ref=${conf_key}
- conf_ref="${conf_val}"
- done
- log "Configuration:"
- for conf_key in ${flag_variables[@]}; do
- local -n conf_ref=${conf_key}
- log "- ${conf_key} = ${conf_ref}"
- done
-}
-
-print_help_and_exit() {
- local conf_key
- echo "Available options: (see script for details)" >&2
- for conf_key in ${flag_variables[@]}; do
- local -n conf_ref=${conf_key}
- echo " --${conf_key}=[${conf_ref}]" >&2
- done
- exit 1
-}
-
-validate_flags_and_augment_globals() {
- arch_packages+=(${kernel_package})
- case "${target_disklabel}" in
- gpt)
- ;;
- dos)
- ;;
- *)
- fatal "Unknown disklabel type: ${target_disklabel}"
- ;;
- esac
- case "${target_filesystem}" in
- ext4)
- ;;
- btrfs)
- host_packages+=(btrfs-tools)
- arch_packages+=(btrfs-progs)
- ;;
- *)
- fatal "Unknown filesystem type: ${target_filesystem}"
- ;;
- esac
- local disk_MiB=$(($(cat /sys/block/vda/size) >> 11))
- archroot_size_MiB=$((disk_MiB - gpt2_size_MiB - archroot_offset_MiB))
-}
-
-read_flags() {
- local filename=$1
- source ${filename}
-}
-
-write_flags() {
- local filename=$1
- {
- local conf_key
- for conf_key in ${flag_variables[@]}; do
- local -n conf_ref=${conf_key}
- printf "%s=%q\n" "${conf_key}" "${conf_ref}"
- done
- } > ${filename}
-}
-
-sanity_checks() {
- [ ${EUID} -eq 0 ] || fatal "Script must be run as root."
- [ ${UID} -eq 0 ] || fatal "Script must be run as root."
- [ -e /dev/vda ] || fatal "Script must be run on a KVM machine."
- [[ "$(cat /etc/debian_version)" == 8.? ]] || \
- fatal "This script only supports Debian 8.x."
-}
-
-prompt_for_destruction() {
- (( continue_without_prompting )) && return 0
- log "*** ALL DATA ON THIS DROPLET WILL BE WIPED. ***"
- log "Please backup all important data on this droplet before continuing."
- log 'Type "wipe this droplet" to continue or anything else to cancel.'
- local response
- read -p ' > ' response
- if [ "${response}" = "wipe this droplet" ]; then
- return 0
- else
- log "Cancelled."
- exit 0
- fi
-}
-
-download_and_verify() {
- local file_url="$1"
- local local_path="$2"
- local expected_sha1="$3"
- for try in {0..3}; do
- if [ ${try} -eq 0 ]; then
- [ -e "${local_path}" ] || continue
- else
- wget -O "${local_path}" "${file_url}"
- fi
- set -- $(sha1sum "${local_path}")
- if [ $1 = "${expected_sha1}" ]; then
- return 0
- else
- rm -f "${local_path}"
- fi
- done
- return 1
-}
-
-build_parted_cmdline() {
- local cmdline=
- local biosboot_name=BIOSBoot
- local doroot_name=DORoot
- local archroot_name=ArchRoot
- if [ ${target_disklabel} = dos ]; then
- cmdline="mklabel msdos"
- biosboot_name=primary
- doroot_name=primary
- archroot_name=primary
- else
- cmdline="mklabel ${target_disklabel}"
- fi
- local archroot_end_MiB=$((archroot_offset_MiB + archroot_size_MiB))
- cmdline+=" mkpart ${doroot_name} ${doroot_offset_MiB}MiB ${biosboot_offset_MiB}MiB"
- cmdline+=" mkpart ${biosboot_name} ${biosboot_offset_MiB}MiB ${archroot_offset_MiB}MiB"
- cmdline+=" mkpart ${archroot_name} ${archroot_offset_MiB}MiB ${archroot_end_MiB}MiB"
- if [ ${target_disklabel} = gpt ]; then
- cmdline+=" set 2 bios_grub on"
- fi
- echo "${cmdline}"
-}
-
-setup_loop_device() {
- local offset_MiB=$1
- local size_MiB=$2
- losetup --find --show --offset ${offset_MiB}MiB --size ${size_MiB}MiB /d2a/work/image
-}
-
-package_digitalocean_synchronize() {
- local destination=$1
-
- extract_embedded_file digitalocean-synchronize.pkg.tar.xz > ${destination}
-}
-
-kill_processes_in_mountpoint() {
- if mountpoint -q $1; then
- fuser -kms $1 || true
- find /proc -maxdepth 2 -name root -lname $1 | \
- grep -o '[0-9]*' | xargs -r kill || true
- fi
-}
-
-quietly_umount() {
- if mountpoint -q $1; then
- umount -d $1
- fi
-}
-
-cleanup_work_directory() {
- kill_processes_in_mountpoint /d2a/work/doroot
- kill_processes_in_mountpoint /d2a/work/archroot
- quietly_umount /d2a/work/doroot
- quietly_umount /d2a/work/archroot/var/cache/pacman/pkg
- quietly_umount /d2a/work/archroot/dev/pts
- quietly_umount /d2a/work/archroot/dev
- quietly_umount /d2a/work/archroot/sys
- quietly_umount /d2a/work/archroot/proc
- quietly_umount /d2a/work/archroot
- rm -rf --one-file-system /d2a/work
-}
-
-stage1_install_exit() {
- set +e
- cleanup_work_directory
-}
-
-stage1_install() {
- trap stage1_install_exit EXIT
- cleanup_work_directory
- mkdir -p /d2a/work
-
- log "Installing required packages ..."
- DEBIAN_FRONTEND=noninteractive apt-get install -y ${host_packages[@]}
-
- log "Partitioning image ..."
- local disk_sectors=$(cat /sys/block/vda/size)
- rm -f /d2a/work/image
- truncate -s $((disk_sectors * sector_size)) /d2a/work/image
- parted /d2a/work/image $(build_parted_cmdline)
-
- log "Formatting image ..."
- local doroot_loop=$(setup_loop_device ${doroot_offset_MiB} ${doroot_size_MiB})
- local archroot_loop=$(setup_loop_device ${archroot_offset_MiB} ${archroot_size_MiB})
- mkfs.ext4 -L DOROOT ${doroot_loop}
- mkfs.${target_filesystem} -L ArchRoot ${archroot_loop}
-
- log "Mounting image ..."
- mkdir -p /d2a/work/{doroot,archroot}
- mount ${doroot_loop} /d2a/work/doroot
- mount ${archroot_loop} /d2a/work/archroot
-
- log "Setting up DOROOT ..."
- mkdir -p /d2a/work/doroot/etc/network
- touch /d2a/work/doroot/etc/network/interfaces
- cat > /d2a/work/doroot/README <<-EOF
- DO NOT TOUCH FILES ON THIS PARTITION.
-
- The DOROOT partition is where DigitalOcean writes passwords and other data
- when a droplet is rebuilt from an image or restored from a snapshot.
- If certain files are missing, restores/rebuilds will not work and you will
- end up with an unusable image.
-
- The digitalocean-synchronize script also watches this partition.
- If this partition (particularly etc/shadow) is written to, the script will
- reset the root password to the one provided by DigitalOcean and wipe all
- SSH host keys for security.
- EOF
- chmod 0444 /d2a/work/doroot/README
-
- log "Downloading bootstrap tarball ..."
- set -- $(wget -qO- ${archlinux_mirror}/iso/latest/sha1sums.txt |
- grep "archlinux-bootstrap-[^-]*-${target_architecture}.tar.gz")
- local expected_sha1=$1
- local bootstrap_filename=$2
- download_and_verify \
- ${archlinux_mirror}/iso/latest/${bootstrap_filename} \
- /d2a/bootstrap.tar.gz \
- ${expected_sha1}
-
- log "Extracting bootstrap tarball ..."
- tar -xzf /d2a/bootstrap.tar.gz \
- --directory=/d2a/work/archroot \
- --strip-components=1
-
- log "Mounting virtual filesystems ..."
- mount -t proc proc /d2a/work/archroot/proc
- mount -t sysfs sys /d2a/work/archroot/sys
- mount -t devtmpfs dev /d2a/work/archroot/dev
- mkdir -p /d2a/work/archroot/dev/pts
- mount -t devpts pts /d2a/work/archroot/dev/pts
-
- log "Binding packages directory ..."
- mkdir -p /d2a/packages
- mount --bind /d2a/packages /d2a/work/archroot/var/cache/pacman/pkg
-
- log "Preparing bootstrap filesystem ..."
- echo "Server = ${archlinux_mirror}/\$repo/os/\$arch" > /d2a/work/archroot/etc/pacman.d/mirrorlist
- echo 'nameserver 8.8.8.8' > /d2a/work/archroot/etc/resolv.conf
-
- log "Installing base system ..."
- chroot /d2a/work/archroot pacman-key --init
- chroot /d2a/work/archroot pacman-key --populate archlinux
- local chroot_pacman="chroot /d2a/work/archroot pacman --arch ${target_architecture}"
- ${chroot_pacman} -Sy
- ${chroot_pacman} -Su --noconfirm --needed \
- $(${chroot_pacman} -Sgq base | grep -v '^linux$') \
- ${arch_packages[@]}
-
- log "Configuring base system ..."
- hostname > /d2a/work/archroot/etc/hostname
- cp /etc/ssh/ssh_host_* /d2a/work/archroot/etc/ssh/
- local encrypted_password=$(awk -F: '$1 == "root" { print $2 }' /etc/shadow)
- chroot /d2a/work/archroot usermod -p "${encrypted_password}" root
- chroot /d2a/work/archroot systemctl enable systemd-networkd.service
- chroot /d2a/work/archroot systemctl enable sshd.service
- package_digitalocean_synchronize /d2a/work/archroot/dosync.pkg.tar
- ${chroot_pacman} -U --noconfirm /dosync.pkg.tar
- rm /d2a/work/archroot/dosync.pkg.tar
-
- log "Finishing up image generation ..."
- ln -f /d2a/work/image /d2a/image
- cleanup_work_directory
- trap - EXIT
-}
-
-bisect_left_on_allocation() {
- local alloc_start_sector=$1
- local alloc_end_sector=$2
- local -n bisection_output=$3
- local -n allocation_map=$4
- local lo=0 hi=${#allocation_map[@]}
- while (( lo < hi )); do
- local mid=$(((lo+hi)/2))
- set -- ${allocation_map[$mid]}
- if (( $# == 0 )) || (( $1 < alloc_start_sector )); then
- lo=$((mid+1))
- else
- hi=$((mid))
- fi
- done
- bisection_output=$lo
-}
-
-check_for_allocation_overlap() {
- local check_start_sector=$1
- local check_end_sector=$2
- local -n overlap_start_sector=$3
- local -n overlap_end_sector=$4
- shift 4
- local allocation_maps="$*"
-
- # overlap_end_sector = 0 if no overlap
- overlap_start_sector=0
- overlap_end_sector=0
-
- local map_name
- for map_name in ${allocation_maps}; do
- local -n allocation_map=${map_name}
- local map_length=${#allocation_map[@]}
- (( ${map_length} )) || continue
- local bisection_index
- bisect_left_on_allocation ${check_start_sector} ${check_end_sector} \
- bisection_index ${map_name}
- local check_index
- for check_index in $((bisection_index - 1)) $((bisection_index)); do
- (( check_index < 0 || check_index >= map_length )) && continue
- set -- ${allocation_map[${check_index}]}
- (( $# == 0 )) && continue
- local alloc_start_sector=$1
- local alloc_end_sector=$2
- (( check_start_sector >= alloc_end_sector || alloc_start_sector >= check_end_sector )) && continue
- # overlap detected
- overlap_start_sector=$((alloc_start_sector > check_start_sector ?
- alloc_start_sector : check_start_sector))
- overlap_end_sector=$((alloc_end_sector < check_end_sector ?
- alloc_end_sector : check_end_sector))
- return
- done
- done
-}
-
-insert_into_allocation_map() {
- local -n allocation_map=$1
- shift
- local alloc_start_sector=$1
- local alloc_end_sector=$2
- if (( ${#allocation_map[@]} == 0 )); then
- allocation_map=("$*")
- else
- local bisection_index
- bisect_left_on_allocation ${alloc_start_sector} ${alloc_end_sector} \
- bisection_index ${!allocation_map}
- allocation_map=(
- "${allocation_map[@]:0:${bisection_index}}"
- "$*"
- "${allocation_map[@]:${bisection_index}}")
- fi
-}
-
-stage2_arrange() {
- local disk_sectors=$(cat /sys/block/vda/size)
- local root_device=$(awk '$2 == "/" { root = $1 } END { print root }' /proc/mounts)
- local root_offset_sectors=$(cat /sys/block/vda/${root_device#/dev/}/start)
- local srcdst_map=() # original source to target map
- local unalloc_map=() # extents not used by either source or target (for tmpdst_map)
- local tmpdst_map=() # extents on temporary redirection (allocated from unalloc_map)
- local source_start_sector source_end_sector target_start_sector target_end_sector
-
- log "Creating block rearrangement plan ..."
-
- # get and sort extents
- filefrag -e -s -v -b${sector_size} /d2a/image | \
- sed '/^ *[0-9]*:/!d;s/[:.]/ /g' | \
- sort -nk4 > /d2a/imagemap
- while read line; do
- set -- ${line}
- source_start_sector=$(($4 + root_offset_sectors))
- source_end_sector=$((source_start_sector + $6))
- target_start_sector=$2
- target_end_sector=$((target_start_sector + $6))
- echo ${source_start_sector} ${source_end_sector}
- echo ${target_start_sector} ${target_end_sector}
- srcdst_map+=("${source_start_sector} ${source_end_sector} ${target_start_sector}")
- done < /d2a/imagemap > /d2a/unsortedallocs
- sort -n < /d2a/unsortedallocs > /d2a/sortedallocs
-
- # build map of unallocated sectors
- local unalloc_start_sector=0 unalloc_end_sector=${disk_sectors}
- while read source_start_sector source_end_sector; do
- if (( source_end_sector <= unalloc_start_sector )); then
- # does not overlap unallocated part
- continue
- elif (( source_start_sector > unalloc_start_sector )); then
- # full overlap with unallocated part
- unalloc_map+=("${unalloc_start_sector} ${source_start_sector}")
- unalloc_start_sector=${source_end_sector}
- else
- # partial overlap
- unalloc_start_sector=${source_end_sector}
- fi
- done < /d2a/sortedallocs
- if (( unalloc_start_sector != unalloc_end_sector )); then
- unalloc_map+=("${unalloc_start_sector} ${unalloc_end_sector}")
- fi
-
- # open blockplan
- exec {blockplan_fd}>/d2a/blockplan
-
- # arrange sectors
- while (( ${#srcdst_map[@]} )); do
- set -- ${srcdst_map[-1]}
- source_start_sector=$1
- source_end_sector=$2
- target_start_sector=$3
- target_end_sector=$((target_start_sector + (source_end_sector - source_start_sector)))
- if (( source_start_sector == target_start_sector )); then
- unset 'srcdst_map[-1]'
- continue
- elif (( target_start_sector >= source_end_sector ||
- source_start_sector >= target_end_sector )); then
- unset 'srcdst_map[-1]'
- else
- local new_extent_sectors=$((target_start_sector - source_start_sector))
- new_extent_sectors=${new_extent_sectors#-} # absolute value
- set -- \
- $((source_start_sector + new_extent_sectors)) \
- $((source_end_sector)) \
- $((target_start_sector + new_extent_sectors))
- srcdst_map[-1]="$*"
- source_end_sector=$((source_start_sector + new_extent_sectors))
- fi
- local overlap_start_sector overlap_end_sector
- check_for_allocation_overlap \
- ${target_start_sector} ${target_end_sector} \
- overlap_start_sector overlap_end_sector \
- srcdst_map
- if (( overlap_end_sector )); then
- # insert non-overlapping parts back into srcdst_map
- if (( target_start_sector < overlap_start_sector )); then
- local nonoverlap_length_sectors=$((overlap_start_sector - target_start_sector))
- insert_into_allocation_map srcdst_map \
- ${source_start_sector} \
- $((source_start_sector + nonoverlap_length_sectors)) \
- ${target_start_sector}
- fi
- if (( target_end_sector > overlap_end_sector )); then
- local nonoverlap_length_sectors=$((target_end_sector - overlap_end_sector))
- insert_into_allocation_map srcdst_map \
- $((source_end_sector - nonoverlap_length_sectors)) \
- ${source_end_sector} \
- ${overlap_end_sector}
- fi
- # copy overlapping portion into tmpdst_map
- while (( overlap_start_sector < overlap_end_sector )); do
- set -- ${unalloc_map[-1]}
- unset 'unalloc_map[-1]' # or nullglob will eat it up
- local unalloc_start_sector=$1
- local unalloc_end_sector=$2
- local unalloc_length_sectors=$((unalloc_end_sector - unalloc_start_sector))
- local overlap_length_sectors=$((overlap_end_sector - overlap_start_sector))
- if (( overlap_length_sectors < unalloc_length_sectors )); then
- # return unused portion to unalloc_map
- unalloc_map+=("${unalloc_start_sector} $((unalloc_end_sector - overlap_length_sectors))")
- unalloc_start_sector=$((unalloc_end_sector - overlap_length_sectors))
- unalloc_length_sectors=${overlap_length_sectors}
- fi
- echo >&${blockplan_fd} \
- $((source_start_sector + (overlap_start_sector - target_start_sector))) \
- ${unalloc_start_sector} \
- ${unalloc_length_sectors}
- insert_into_allocation_map tmpdst_map \
- ${unalloc_start_sector} \
- ${unalloc_end_sector} \
- ${overlap_start_sector}
- (( overlap_start_sector += unalloc_length_sectors ))
- done
- else
- echo >&${blockplan_fd} \
- ${source_start_sector} \
- ${target_start_sector} \
- $((source_end_sector - source_start_sector))
- fi
- done
-
- # restore overlapped sectors
- while (( ${#tmpdst_map[@]} )); do
- set -- ${tmpdst_map[-1]}
- unset 'tmpdst_map[-1]'
- source_start_sector=$1
- source_end_sector=$2
- target_start_sector=$3
- echo >&${blockplan_fd} \
- ${source_start_sector} \
- ${target_start_sector} \
- $((source_end_sector - source_start_sector))
- done
-
- # close blockplan
- exec {blockplan_fd}>&-
-}
-
-cleanup_mid_directory() {
- quietly_umount /d2a/mid
- rm -rf --one-file-system /d2a/mid
-}
-
-add_binary_to_mid() {
- mkdir -p $(dirname /d2a/mid/$1)
- cp $1 /d2a/mid/$1
- ldd $1 | grep -o '/[^ ]* (0x[0-9a-f]*)' | \
- while read libpath ignored; do
- [ -e /d2a/mid/${libpath} ] && continue
- mkdir -p $(dirname /d2a/mid/${libpath})
- cp ${libpath} /d2a/mid/${libpath}
- done
-}
-
-stage3_prepare_exit() {
- set +e
- cleanup_mid_directory
-}
-
-stage3_prepare() {
- trap stage3_prepare_exit EXIT
- cleanup_mid_directory
- mkdir -p /d2a/mid
-
- # mount tmpfs
- mount -t tmpfs mid /d2a/mid
-
- # add binaries
- add_binary_to_mid /bin/busybox
- add_binary_to_mid /bin/bash
-
- # create symlinks
- local dir
- for dir in bin sbin usr/bin usr/sbin; do mkdir -p /d2a/mid/${dir}; done
- ln -s bash /d2a/mid/bin/sh
- chroot /d2a/mid /bin/busybox --install
-
- # create directories (will be filled by systemd)
- mkdir /d2a/mid/{proc,sys,dev}
-
- # copy in the blockplan
- cp /d2a/blockplan /d2a/mid/blockplan
-
- # write out flags
- write_flags /d2a/mid/flags
-
- # copy myself
- cat "$0" > /d2a/mid/init
- chmod 0755 /d2a/mid/init
-
- # detach all loop devices
- losetup -D || true
-
- # reboot!
- log "The machine will now reboot."
- log "Check the console for errors if the machine is still unaccessible after a few minutes."
- sleep 1
- trap - EXIT
- systemctl switch-root /d2a/mid /init
-}
-
-stage4_convert_exit() {
- log "Error occurred. You're on your own!"
- exec /bin/bash </dev/console >/dev/console 2>&1
-}
-
-stage4_convert() {
- trap stage4_convert_exit EXIT
-
- # unmount old root
- local retry
- for retry in 1 2 3 4 5; do
- if umount /mnt; then
- retry=0
- break
- else
- sleep 1
- fi
- done
- if (( retry )); then
- umount -rl /mnt
- fi
-
- # get total number of sectors
- local processed_length=0
- local total_length=$(awk '{x+=$3}END{print+x}' /blockplan)
- local prev_percentage=-1
- local next_percentage=-1
-
- # execute the block plan
- local source_sector target_sector extent_length
- while read source_sector target_sector extent_length; do
- # increment processed length before extent length gets optimized
- (( processed_length += extent_length )) || true
- # optimize extent length
- local transfer_size=${sector_size}
- until (( (source_sector & 1) || (target_sector & 1) ||
- (extent_length & 1) || (transfer_size >= 0x100000) )); do
- (( source_sector >>= 1 , target_sector >>= 1 , extent_length >>= 1,
- transfer_size <<= 1 )) || true
- done
- # do the actual transfer
- dd if=/dev/vda of=/dev/vda bs=${transfer_size} \
- skip=${source_sector} seek=${target_sector} \
- count=${extent_length} 2>/dev/null
- # print out the percentage
- next_percentage=$((100 * processed_length / total_length))
- if (( next_percentage != prev_percentage )); then
- printf "\rTransferring blocks ... %s%%" ${next_percentage}
- prev_percentage=${next_percentage}
- fi
- done < /blockplan
- echo
-
- # reread partition table
- blockdev --rereadpt /dev/vda
-
- # install bootloader
- mkdir /archroot
- mount /dev/vda3 /archroot
- mount -t proc proc /archroot/proc
- mount -t sysfs sys /archroot/sys
- mount -t devtmpfs dev /archroot/dev
- chroot /archroot grub-mkconfig -o /boot/grub/grub.cfg
- chroot /archroot grub-install /dev/vda
- umount /archroot/dev
- umount /archroot/sys
- umount /archroot/proc
- umount /archroot
-
- # we're done!
- sync
- reboot -f
-}
-
-reinstall_digitalocean_synchronize() {
- local package_file=$(mktemp --suffix=.pkg.tar)
- package_digitalocean_synchronize ${package_file}
- pacman -U --noconfirm ${package_file}
- rm ${package_file}
-}
-
-if [ -e /var/lib/pacman ]; then
- if [ $# -eq 0 ]; then
- reinstall_digitalocean_synchronize
- else
- log "Run this script to install/update the digitalocean-synchronize package."
- fi
- exit 0
-fi
-
-if [ $$ -ne 1 ]; then
- parse_flags "$@"
- sanity_checks
- validate_flags_and_augment_globals
- prompt_for_destruction
- stage1_install
- stage2_arrange
- stage3_prepare
-else
- read_flags /flags
- validate_flags_and_augment_globals
- stage4_convert
-fi
-exit 0