X-Git-Url: https://code.grnet.gr/git/snf-image/blobdiff_plain/8576747bfa59c514902d0095c3ba991e17673595..84d01e3e70a54b38c693c67df075067099e32462:/snf-image-helper/common.sh diff --git a/snf-image-helper/common.sh b/snf-image-helper/common.sh index 45ec606..e17ef2a 100644 --- a/snf-image-helper/common.sh +++ b/snf-image-helper/common.sh @@ -1,4 +1,4 @@ -# Copyright (C) 2011 GRNET S.A. +# Copyright (C) 2011, 2012, 2013 GRNET S.A. # Copyright (C) 2007, 2008, 2009 Google Inc. # # This program is free software; you can redistribute it and/or modify @@ -16,8 +16,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. -RESULT=/dev/ttyS1 -FLOPPY_DEV=/dev/fd0 PROGNAME=$(basename $0) PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin @@ -33,8 +31,21 @@ BLKID=blkid BLOCKDEV=blockdev REGLOOKUP=reglookup CHNTPW=chntpw +SGDISK=sgdisk +GROWFS_UFS=growfs.ufs +DUMPFS_UFS=dumpfs.ufs +DATE="date -u" # Time in UTC +EATMYDATA=eatmydata +MOUNT="mount -n" CLEANUP=( ) +ERRORS=( ) +WARNINGS=( ) + +MSG_TYPE_TASK_START="TASK_START" +MSG_TYPE_TASK_END="TASK_END" + +STDERR_LINE_SIZE=10 add_cleanup() { local cmd="" @@ -42,13 +53,127 @@ add_cleanup() { CLEANUP+=("$cmd") } +close_fd() { + local fd=$1 + + exec {fd}>&- +} + +send_result_kvm() { + echo "$@" > /dev/ttyS1 +} + +send_monitor_message_kvm() { + echo "$@" > /dev/ttyS2 +} + +send_result_xen() { + xenstore-write /local/domain/0/snf-image-helper/$DOMID "$*" +} + +send_monitor_message_xen() { + #Broadcast the message + echo "$@" | socat "STDIO" "UDP-DATAGRAM:${BROADCAST}:${MONITOR_PORT},broadcast" +} + +prepare_helper() { + local cmdline item key val hypervisor domid + + read -a cmdline < /proc/cmdline + for item in "${cmdline[@]}"; do + key=$(cut -d= -f1 <<< "$item") + val=$(cut -d= -f2 <<< "$item") + if [ "$key" = "hypervisor" ]; then + hypervisor="$val" + fi + if [ "$key" = "rules_dev" ]; then + export RULES_DEV="$val" + fi + if [ "$key" = "helper_ip" ]; then + export IP="$val" + export NETWORK="$IP/24" + export BROADCAST="${IP%.*}.255" + fi + if [ "$key" = "monitor_port" ]; then + export MONITOR_PORT="$val" + fi + done + + case "$hypervisor" in + kvm) + HYPERVISOR=kvm + ;; + xen-hvm|xen-pvm) + if [ -z "$IP" ]; then + echo "ERROR: \`helper_ip' not defined or empty" >&2 + exit 1 + fi + if [ -z "$MONITOR_PORT" ]; then + echo "ERROR: \`monitor_port' not defined or empty" >&2 + exit 1 + fi + $MOUNT -t xenfs xenfs /proc/xen + ip addr add "$NETWORK" dev eth0 + ip link set eth0 up + ip route add default dev eth0 + export DOMID=$(xenstore-read domid) + HYPERVISOR=xen + ;; + *) + echo "ERROR: Unknown hypervisor: \`$hypervisor'" >&2 + exit 1 + ;; + esac + + export HYPERVISOR +} + +report_error() { + msg="" + if [ ${#ERRORS[*]} -eq 0 ]; then + # No error message. Print stderr + local lines stderr + stderr="$(tail --lines=${STDERR_LINE_SIZE} "$STDERR_FILE")" + lines=$(wc -l <<< "$stderr") + msg="STDERR:${lines}:$stderr" + else + for line in "${ERRORS[@]}"; do + msg+="ERROR:$line"$'\n' + done + fi + + send_monitor_message_${HYPERVISOR} "$msg" +} + log_error() { - echo "ERROR: $@" | tee $RESULT >&2 - exit 1 + ERRORS+=("$*") + + send_result_${HYPERVISOR} "ERROR: $@" + + # Use return instead of exit. The set -x options will terminate the script + # but will also trigger ERR traps if defined. + return 1 } warn() { echo "Warning: $@" >&2 + send_monitor_message_${HYPERVISOR} "WARNING: $@" +} + +report_task_start() { + send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_START:${PROGNAME:2}" +} + +report_task_end() { + send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_END:${PROGNAME:2}" +} + +system_poweroff() { + while [ 1 ]; do + # Credits to psomas@grnet.gr for this ... + echo o > /proc/sysrq-trigger + sleep 1 + done } get_base_distro() { @@ -64,13 +189,18 @@ get_base_distro() { echo "suse" elif [ -e "$root_dir/etc/gentoo-release" ]; then echo "gentoo" + elif [ -e "$root_dir/etc/arch-release" ]; then + echo "arch" + elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then + echo "freebsd" else warn "Unknown base distro." fi } get_distro() { - local root_dir=$1 + local root_dir distro + root_dir=$1 if [ -e "$root_dir/etc/debian_version" ]; then distro="debian" @@ -93,6 +223,10 @@ get_distro() { echo "suse" elif [ -e "$root_dir/etc/gentoo-release" ]; then echo "gentoo" + elif [ -e "$root_dir/etc/arch-release" ]; then + echo "arch" + elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then + echo "freebsd" else warn "Unknown distro." fi @@ -100,22 +234,23 @@ get_distro() { get_partition_table() { - local dev="$1" + local dev output + dev="$1" # If the partition table is gpt then parted will raise an error if the # secondary gpt is not it the end of the disk, and a warning that has to # do with the "Last Usable LBA" entry in gpt. if ! output="$("$PARTED" -s -m "$dev" unit s print | grep -E -v "^(Warning|Error): ")"; then - log_error "Unable to read partition table for device \`${dev}'" + log_error "Unable to read partition table for device \`${dev}'. The image seems corrupted." fi echo "$output" } get_partition_table_type() { - local ptable="$1" + local ptable dev field + ptable="$1" - local dev="$(sed -n 2p <<< "$ptable")" - declare -a field + dev="$(sed -n 2p <<< "$ptable")" IFS=':' read -ra field <<< "$dev" echo "${field[5]}" @@ -144,8 +279,8 @@ is_extended_partition() { local dev="$1" local part_num="$2" - id=$($SFDISK --print-id "$dev" "$part_num") - if [ "$id" = "5" ]; then + id=$($SFDISK --force --print-id "$dev" "$part_num") + if [ "$id" = "5" -o "$id" = "f" ]; then echo "yes" else echo "no" @@ -153,8 +288,9 @@ is_extended_partition() { } get_extended_partition() { - local ptable="$1" - local dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)" + local ptable dev + ptable="$1" + dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)" tail -n +3 <<< "$ptable" | while read line; do part_num=$(cut -d':' -f1 <<< "$line") @@ -167,7 +303,8 @@ get_extended_partition() { } get_logical_partitions() { - local ptable="$1" + local ptable part_num + ptable="$1" tail -n +3 <<< "$ptable" | while read line; do part_num=$(cut -d':' -f1 <<< "$line") @@ -180,8 +317,9 @@ get_logical_partitions() { } get_last_primary_partition() { - local ptable="$1" - local dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1) + local ptable dev output + ptable="$1" + dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1) for i in 4 3 2 1; do if output=$(grep "^$i:" <<< "$ptable"); then @@ -193,9 +331,14 @@ get_last_primary_partition() { } get_partition_to_resize() { - local dev="$1" + local dev table table_type last_part last_part_num extended last_primary \ + ext_num prim_num + dev="$1" table=$(get_partition_table "$dev") + if [ -z "$table" ]; then + return 0 + fi if [ $(get_partition_count "$table") -eq 0 ]; then return 0 @@ -209,7 +352,7 @@ get_partition_to_resize() { extended=$(get_extended_partition "$table") last_primary=$(get_last_primary_partition "$table") ext_num=$(cut -d: -f1 <<< "$extended") - prim_num=$(cut -d: -f1 <<< "$last_primary") + last_prim_num=$(cut -d: -f1 <<< "$last_primary") if [ "$ext_num" != "$last_prim_num" ]; then echo "$last_prim_num" @@ -226,7 +369,7 @@ create_partition() { local part="$2" local ptype="$3" - declare -a fields + local fields=() IFS=":;" read -ra fields <<< "$part" local id="${fields[0]}" local start="${fields[1]}" @@ -236,27 +379,35 @@ create_partition() { local name="${fields[5]}" local flags="${fields[6]//,/ }" - $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end" - for flag in $flags; do - $PARTED -s -m $device set "$id" "$flag" on - done + if [ "$ptype" = "primary" -o "$ptype" = "logical" -o "$ptype" = "extended" ]; then + $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end" + for flag in $flags; do + $PARTED -s -m $device set "$id" "$flag" on + done + else + # For gpt + start=${start:0:${#start}-1} # remove the s at the end + end=${end:0:${#end}-1} # remove the s at the end + $SGDISK -n "$id":"$start":"$end" -t "$id":"$ptype" "$device" + fi } enlarge_partition() { - local device="$1" - local part="$2" - local ptype="$3" - local new_end="$4" + local device part ptype new_end fields new_part table logical id + device="$1" + part="$2" + ptype="$3" + new_end="$4" if [ -z "$new_end" ]; then new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")") fi - declare -a fields + fields=() IFS=":;" read -ra fields <<< "$part" fields[2]="$new_end" - local new_part="" + new_part="" for ((i = 0; i < ${#fields[*]}; i = i + 1)); do new_part="$new_part":"${fields[$i]}" done @@ -265,8 +416,8 @@ enlarge_partition() { # If this is an extended partition, removing it will also remove the # logical partitions it contains. We need to save them for later. if [ "$ptype" = "extended" ]; then - local table="$(get_partition_table "$device")" - local logical="$(get_logical_partitions "$table")" + table="$(get_partition_table "$device")" + logical="$(get_logical_partitions "$table")" fi id=${fields[0]} @@ -282,21 +433,67 @@ enlarge_partition() { } get_last_free_sector() { - local dev="$1" - local unit="$2" + local dev unit last_line ptype + dev="$1" + unit="$2" if [ -n "$unit" ]; then unit="unit $unit" fi - local last_line="$("$PARTED" -s -m "$dev" "$unit" print free | tail -1)" - local ptype="$(cut -d: -f 5 <<< "$last_line")" + last_line="$($PARTED -s -m "$dev" "$unit" print free | tail -1)" + ptype="$(cut -d: -f 5 <<< "$last_line")" if [ "$ptype" = "free;" ]; then echo "$last_line" fi } +get_unattend() { + local target exists + target="$1" + + # Workaround to search for $target/Unattend.xml in an case insensitive way. + exists=$(find "$target"/ -maxdepth 1 -iname unattend.xml) + if [ $(wc -l <<< "$exists") -gt 1 ]; then + log_error "Found multiple Unattend.xml files in the image:" $exists + fi + + echo "$exists" +} + +umount_all() { + local target mpoints + target="$1" + + # Unmount file systems mounted under directory `target' + mpoints="$({ awk "{ if (match(\$2, \"^$target\")) { print \$2 } }" < /proc/mounts; } | sort -rbd | uniq)" + + for mpoint in $mpoints; do + umount $mpoint + done +} + +get_ufstype() { + local device ufs + + device="$1" + ufs="$($DUMPFS_UFS "$device" | head -1 | awk '{ match ($3, /\((.+)\)/, ufs); print ufs[1] }')" + + case "$ufs" in + UFS1) + echo 44bsd + ;; + UFS2) + echo ufs2 + ;; + *) + log_error "Unsupported UFS type: \`$ufs' in device $device" + echo "" + ;; + esac +} + cleanup() { # if something fails here, it shouldn't call cleanup again... trap - EXIT @@ -329,18 +526,39 @@ cleanup() { fi } -check_if_excluded() { +task_cleanup() { + local rc=$? - local exclude=SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${PROGNAME:2} + if [ $rc -eq 0 ]; then + report_task_end + else + report_error + fi + + cleanup +} + +check_if_excluded() { + local name exclude + name="$(tr [a-z] [A-Z] <<< ${PROGNAME:2})" + exclude="SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${name}" if [ -n "${!exclude}" ]; then - warn "Task $PROGNAME was excluded and will not run." + warn "Task ${PROGNAME:2} was excluded and will not run." exit 0 fi return 0 } +return_success() { + send_result_${HYPERVISOR} "SUCCESS" +} + trap cleanup EXIT set -o pipefail +STDERR_FILE=$(mktemp) +add_cleanup rm -f "$STDERR_FILE" +exec 2> >(tee -a "$STDERR_FILE" >&2) + # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :