|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Library to install software on Raspbian ARM through QEMU |
| 4 | +# |
| 5 | +# Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com> |
| 6 | +# GPL licensed (see end of file) * Use at your own risk! |
| 7 | +# |
| 8 | +# More at ownyourbits.com |
| 9 | +# |
| 10 | + |
| 11 | +IMGNAME=$( basename "$IMGFILE" .img )_$( basename "$INSTALL_SCRIPT" .sh ).img |
| 12 | +DBG=x |
| 13 | + |
| 14 | +# $IMGOUT will contain the name of the last step |
| 15 | +function launch_install_qemu() |
| 16 | +{ |
| 17 | + local IMG=$1 |
| 18 | + local IP=$2 |
| 19 | + [[ "$IP" == "" ]] && { echo "usage: launch_install_qemu <script> <img> <IP>"; return 1; } |
| 20 | + test -f "$IMG" || { echo "input file $IMG not found"; return 1; } |
| 21 | + |
| 22 | + local BASE=$( sed 's=-stage[[:digit:]]==' <<< "$IMG" ) |
| 23 | + local NUM=$( sed 's=.*-stage\([[:digit:]]\)=\1=' <<< "$IMG" ) |
| 24 | + [[ "$BASE" == "$IMG" ]] && NUM=0 |
| 25 | + |
| 26 | + local NUM_REBOOTS=$( grep -c reboot "$INSTALL_SCRIPT" ) |
| 27 | + while [[ $NUM_REBOOTS != -1 ]]; do |
| 28 | + NUM=$(( NUM+1 )) |
| 29 | + IMGOUT="$BASE-stage$NUM" |
| 30 | + cp -v "$IMG" "$IMGOUT" || return 1 # take a copy of the input image for processing ( append "-stage1" ) |
| 31 | + |
| 32 | + pgrep qemu-system-arm &>/dev/null && { echo -e "QEMU instance already running. Abort..."; return 1; } |
| 33 | + launch_qemu "$IMGOUT" & |
| 34 | + sleep 10 |
| 35 | + wait_SSH "$IP" |
| 36 | + launch_installation_qemu "$IP" || return 1 |
| 37 | + wait |
| 38 | + IMG="$IMGOUT" |
| 39 | + NUM_REBOOTS=$(( NUM_REBOOTS-1 )) |
| 40 | + done |
| 41 | + echo "$IMGOUT generated successfully" |
| 42 | +} |
| 43 | + |
| 44 | +function launch_qemu() |
| 45 | +{ |
| 46 | + local IMG=$1 |
| 47 | + test -f "$1" || { echo "Image $IMG not found"; return 1; } |
| 48 | + test -d qemu-raspbian-network || git clone https://github.com/nachoparker/qemu-raspbian-network.git |
| 49 | + sed -i '30s/NO_NETWORK=1/NO_NETWORK=0/' qemu-raspbian-network/qemu-pi.sh |
| 50 | + sed -i '35s/NO_GRAPHIC=0/NO_GRAPHIC=1/' qemu-raspbian-network/qemu-pi.sh |
| 51 | + echo "Starting QEMU image $IMG" |
| 52 | + ( cd qemu-raspbian-network && sudo ./qemu-pi.sh ../"$IMG" 2>/dev/null ) |
| 53 | +} |
| 54 | + |
| 55 | +function ssh_pi() |
| 56 | +{ |
| 57 | + local IP=$1 |
| 58 | + local ARGS=${@:2} |
| 59 | + local PIUSER=${PIUSER:-pi} |
| 60 | + local PIPASS=${PIPASS:-raspberry} |
| 61 | + local SSH=( ssh -q -o UserKnownHostsFile=/dev/null\ |
| 62 | + -o StrictHostKeyChecking=no\ |
| 63 | + -o ServerAliveInterval=20\ |
| 64 | + -o ConnectTimeout=20\ |
| 65 | + -o LogLevel=quiet ) |
| 66 | + type sshpass &>/dev/null && local SSHPASS=( sshpass -p$PIPASS ) |
| 67 | + if [[ "${SSHPASS[@]}" == "" ]]; then |
| 68 | + ${SSH[@]} ${PIUSER}@$IP $ARGS; |
| 69 | + else |
| 70 | + ${SSHPASS[@]} ${SSH[@]} ${PIUSER}@$IP $ARGS |
| 71 | + local RET=$? |
| 72 | + [[ $RET -eq 5 ]] && { ${SSH[@]} ${PIUSER}@$IP $ARGS; return $?; } |
| 73 | + return $RET |
| 74 | + fi |
| 75 | +} |
| 76 | + |
| 77 | +function wait_SSH() |
| 78 | +{ |
| 79 | + local IP=$1 |
| 80 | + echo "Waiting for SSH to be up on $IP..." |
| 81 | + while true; do |
| 82 | + ssh_pi "$IP" : && break |
| 83 | + sleep 1 |
| 84 | + done |
| 85 | + echo "SSH is up" |
| 86 | +} |
| 87 | + |
| 88 | +function launch_installation() |
| 89 | +{ |
| 90 | + local IP=$1 |
| 91 | + [[ "$INSTALLATION_CODE" == "" ]] && { echo "Need to run config first" ; return 1; } |
| 92 | + [[ "$INSTALLATION_STEPS" == "" ]] && { echo "No installation instructions"; return 1; } |
| 93 | + local PREINST_CODE=" |
| 94 | +set -e$DBG |
| 95 | +sudo su |
| 96 | +set -e$DBG |
| 97 | +" |
| 98 | + echo "Launching installation" |
| 99 | + echo -e "$PREINST_CODE\n$INSTALLATION_CODE\n$INSTALLATION_STEPS" | ssh_pi "$IP" || { echo "Installation to $IP failed" && return 1; } |
| 100 | +} |
| 101 | + |
| 102 | +function launch_installation_qemu() |
| 103 | +{ |
| 104 | + local IP=$1 |
| 105 | + [[ "$NO_CFG_STEP" != "1" ]] && local CFG_STEP=configure |
| 106 | + [[ "$NO_CLEANUP" != "1" ]] && local CLEANUP_STEP=cleanup |
| 107 | + [[ "$NO_HALT_STEP" != "1" ]] && local HALT_STEP="nohup halt &>/dev/null &" |
| 108 | + local INSTALLATION_STEPS=" |
| 109 | +install |
| 110 | +$CFG_STEP |
| 111 | +$CLEANUP_STEP |
| 112 | +$HALT_STEP |
| 113 | +" |
| 114 | + launch_installation "$IP" |
| 115 | +} |
| 116 | + |
| 117 | +function launch_installation_online() |
| 118 | +{ |
| 119 | + local IP=$1 |
| 120 | + [[ "$NO_CFG_STEP" != "1" ]] && local CFG_STEP=configure |
| 121 | + local INSTALLATION_STEPS=" |
| 122 | +install |
| 123 | +$CFG_STEP |
| 124 | +" |
| 125 | + launch_installation "$IP" |
| 126 | +} |
| 127 | + |
| 128 | +function copy_to_image() |
| 129 | +{ |
| 130 | + local IMG=$1 |
| 131 | + local DST=$2 |
| 132 | + local SRC=${@: 3 } |
| 133 | + local SECTOR |
| 134 | + local OFFSET |
| 135 | + SECTOR=$( fdisk -l "$IMG" | grep Linux | awk '{ print $2 }' ) |
| 136 | + OFFSET=$(( SECTOR * 512 )) |
| 137 | + |
| 138 | + [ -f "$IMG" ] || { echo "no image"; return 1; } |
| 139 | + mkdir -p tmpmnt |
| 140 | + sudo mount "$IMG" -o offset="$OFFSET" tmpmnt || return 1 |
| 141 | + sudo cp -v "$SRC" tmpmnt/"$DST" || return 1 |
| 142 | + sudo umount -l tmpmnt |
| 143 | + rmdir tmpmnt &>/dev/null |
| 144 | +} |
| 145 | + |
| 146 | +function deactivate_unattended_upgrades() |
| 147 | +{ |
| 148 | + local IMG=$1 |
| 149 | + local SECTOR |
| 150 | + local OFFSET |
| 151 | + SECTOR=$( fdisk -l "$IMG" | grep Linux | awk '{ print $2 }' ) |
| 152 | + OFFSET=$(( SECTOR * 512 )) |
| 153 | + |
| 154 | + [ -f "$IMG" ] || { echo "no image"; return 1; } |
| 155 | + mkdir -p tmpmnt |
| 156 | + sudo mount "$IMG" -o offset="$OFFSET" tmpmnt || return 1 |
| 157 | + sudo rm -f tmpmnt/etc/apt/apt.conf.d/20nextcloudpi-upgrades |
| 158 | + sudo umount -l tmpmnt |
| 159 | + rmdir tmpmnt &>/dev/null |
| 160 | +} |
| 161 | + |
| 162 | +function download_resize_raspbian_img() |
| 163 | +{ |
| 164 | + local SIZE=$1 |
| 165 | + local IMGFILE=$2 |
| 166 | + local IMG=raspbian_lite_latest |
| 167 | + |
| 168 | + test -f "$IMGFILE" && \ |
| 169 | + echo -e "INFO: $IMGFILE already exists. Skipping download ..." && return 0 |
| 170 | + |
| 171 | + test -f $IMG.zip || \ |
| 172 | + wget https://downloads.raspberrypi.org/$IMG -O $IMG.zip || return 1 |
| 173 | + |
| 174 | + unzip -o $IMG.zip && \ |
| 175 | + mv *-raspbian-*.img "$IMGFILE" && \ |
| 176 | + qemu-img resize -f raw "$IMGFILE" +"$SIZE" && \ |
| 177 | + return 0 |
| 178 | +} |
| 179 | + |
| 180 | +function pack_image() |
| 181 | +{ |
| 182 | + local IMGOUT="$1" |
| 183 | + local IMGNAME="$2" |
| 184 | + local TARNAME=$( basename $IMGNAME .img ).tar.bz2 |
| 185 | + echo "copying $IMGOUT → $IMGNAME" |
| 186 | + cp "$IMGOUT" "$IMGNAME" || return 1 |
| 187 | + echo "packing $IMGNAME → $TARNAME" |
| 188 | + tar -I pbzip2 -cvf "$TARNAME" "$IMGNAME" &>/dev/null && \ |
| 189 | + echo -e "$TARNAME packed successfully" |
| 190 | +} |
| 191 | + |
| 192 | +function create_torrent() |
| 193 | +{ |
| 194 | + [[ "$1" == "" ]] && { echo "No directory specified"; exit 1; } |
| 195 | + test -d "$1" || { echo "$1 not found or is not a directory" ; exit 1; } |
| 196 | + |
| 197 | + md5sum "$1"/*.bz2 > "$1"/md5sum |
| 198 | + |
| 199 | + createtorrent -a udp://tracker.opentrackr.org -p 1337 -c "NextCloudPi. Nextcloud for Raspberry Pi image" "$1" "$1".torrent |
| 200 | +} |
| 201 | + |
| 202 | +function generate_changelog() |
| 203 | +{ |
| 204 | + git log --graph --oneline --decorate \ |
| 205 | + --pretty=format:"[%<(13)%D](https://github.com/nextcloud/nextcloudpi/commit/%h) (%ad) %s" --date=short | \ |
| 206 | + grep 'tag: v' | \ |
| 207 | + sed '/HEAD ->\|origin/s|\[.*\(tag: v[0-9]\+\.[0-9]\+\.[0-9]\+\).*\]|[\1]|' | \ |
| 208 | + sed 's|* \[tag: |\n[|' > changelog.md |
| 209 | +} |
0 commit comments