diff --git a/.gitignore b/.gitignore index 2db707d..b91c2f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ out/ *.rpm *.deb +.vagrant*/ diff --git a/generate-all.sh b/generate-all.sh index 01fa360..3460951 100755 --- a/generate-all.sh +++ b/generate-all.sh @@ -1,4 +1,4 @@ -TARGETS='el6 el6_ec2 el7 el7_ec2 suse deb deb_ec2' +TARGETS='el6 el6_ec2 el7 el7_ec2 suse deb deb_ec2 alpine' set -e for TARGET in $TARGETS; do diff --git a/generate.sh b/generate.sh index de6ee26..4080500 100755 --- a/generate.sh +++ b/generate.sh @@ -53,6 +53,9 @@ RELEASE_FULL="${RELEASE}${RELSUFFIX}" if [ "${TYPE}" = 'deb' ]; then FILENAME="${NAME}_${VERSION}-${RELEASE_FULL}.${TYPE}" +elif [ "${TYPE}" = 'apk' ]; then + RELEASE_FULL="r${RELEASE_FULL}" + FILENAME="${NAME}-${VERSION}-${RELEASE_FULL}.${TYPE}" else FILENAME="${NAME}-${VERSION}-${RELEASE_FULL}.noarch.${TYPE}" fi diff --git a/postinstall.one b/postinstall.one index eaed987..24fb1bc 100755 --- a/postinstall.one +++ b/postinstall.one @@ -16,42 +16,50 @@ # limitations under the License. # #--------------------------------------------------------------------------- # +SERVICES='one-context-local one-context' + rm -f /etc/udev/rules.d/70-persistent-cd.rules rm -f /etc/udev/rules.d/70-persistent-net.rules # Reload udev rules udevadm control --reload >/dev/null 2>&1 || : -# Register service +# Enable services if which systemctl >/dev/null 2>&1 && \ [ -d /etc/systemd ] && \ [ -f /usr/lib/systemd/system/one-context.service ]; then - systemctl enable one-context-local.service - systemctl enable one-context.service + for S in ${SERVICES}; do + systemctl enable "${S}.service" + done fi -if [ -f /etc/init.d/one-context ]; then - if [ -d /etc/sysconfig/network-scripts ] || [ -d /etc/sysconfig/network ]; then - chkconfig --add one-context-local - chkconfig --add one-context - - # EL6: refresh initramfs via dracut for growroot module - if [ -f /usr/share/dracut/modules.d/50growroot/install ]; then - for I in $(find /boot -name 'initramfs-*.img'); do - KERNEL_VERS=$(echo "${I}" | sed -e 's/.*initramfs-\(.*\)\.img/\1/') - dracut -f "${I}" "${KERNEL_VERS}" || : - done - fi - - elif [ -d /etc/network ]; then - for S in one-context-local one-context; do - update-rc.d ${S} enable >/dev/null 2>&1 - update-rc.d ${S} defaults >/dev/null 2>&1 +if which chkconfig >/dev/null 2>&1; then + for S in ${SERVICES}; do + chkconfig --add "${S}" >/dev/null 2>&1 + done + + # EL6: refresh initramfs via dracut for growroot module + if [ -f /usr/share/dracut/modules.d/50growroot/install ]; then + for I in $(find /boot -name 'initramfs-*.img'); do + KERNEL_VERS=$(echo "${I}" | sed -e 's/.*initramfs-\(.*\)\.img/\1/') + dracut -f "${I}" "${KERNEL_VERS}" || : done - else - echo 'WARNING: Contextualization service not enabled' >&2 fi + +elif which update-rc.d >/dev/null 2>&1; then + for S in ${SERVICES}; do + update-rc.d "${S}" enable >/dev/null 2>&1 + update-rc.d "${S}" defaults >/dev/null 2>&1 + done + +elif which rc-update >/dev/null 2>&1; then + for S in ${SERVICES}; do + rc-update add "${S}" boot >/dev/null 2>&1 + done + +else + echo 'WARNING: Contextualization service not enabled automatically' >&2 fi # Debian based distros diff --git a/preuninstall.one b/preuninstall.one index a55d912..bbe53a9 100755 --- a/preuninstall.one +++ b/preuninstall.one @@ -16,18 +16,29 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -# Unregister service -if [ -d /etc/system/ ]; then - systemctl --no-reload disable one-context-local.service one-context.service >/dev/null 2>&1 || : +SERVICES='one-context one-context-local' + +# Disable services +if which systemctl >/dev/null 2>&1 && [ -d /etc/systemd ]; then + for S in ${SERVICES}; do + systemctl --no-reload disable "${S}.service" >/dev/null 2>&1 || : + done + systemctl daemon-reload >/dev/null 2>&1 || : fi -if [ -f /etc/init.d/one-context ]; then - if [ -d /etc/sysconfig/network-scripts ]; then - chkconfig --del one-context >/dev/null 2>&1 || : - chkconfig --del one-context-local >/dev/null 2>&1 || : - elif [ -d /etc/network ]; then - update-rc.d -f one-context remove >/dev/null 2>&1 || : - update-rc.d -f one-context-local remove >/dev/null 2>&1 || : - fi +if which chkconfig >/dev/null 2>&1; then + for S in ${SERVICES}; do + chkconfig --del "${S}" >/dev/null 2>&1 || : + done + +elif which update-rc.d >/dev/null 2>&1; then + for S in ${SERVICES}; do + update-rc.d -f "${S}" remove >/dev/null 2>&1 || : + done + +elif which rc-update >/dev/null 2>&1; then + for S in ${SERVICES}; do + rc-update del "${S}" boot >/dev/null 2>&1 || : + done fi diff --git a/src/etc/init.d/one-context##apk b/src/etc/init.d/one-context##apk new file mode 100755 index 0000000..67cb8a8 --- /dev/null +++ b/src/etc/init.d/one-context##apk @@ -0,0 +1,16 @@ +#!/sbin/openrc-run + +name="OpenNebula contextualization" + +depend() { + need one-context-local net + before sshd + use logger + keyword -stop -shutdown +} + +start() { + ebegin "Starting one-context" + /usr/sbin/one-contextd network + eend $? +} diff --git a/src/etc/init.d/one-context-local##apk b/src/etc/init.d/one-context-local##apk new file mode 100755 index 0000000..1eba8c4 --- /dev/null +++ b/src/etc/init.d/one-context-local##apk @@ -0,0 +1,16 @@ +#!/sbin/openrc-run + +name="OpenNebula pre-networking contextualization" + +depend() { + need localmount udev udev-trigger udev-settle + before net iptables keepalived + use logger + keyword -stop -shutdown +} + +start() { + ebegin "Starting one-context-local" + /usr/sbin/one-contextd local + eend $? +} diff --git a/src/etc/init.d/one-context-reconfigure##apk b/src/etc/init.d/one-context-reconfigure##apk new file mode 100755 index 0000000..5aa8afa --- /dev/null +++ b/src/etc/init.d/one-context-reconfigure##apk @@ -0,0 +1,15 @@ +#!/sbin/openrc-run + +name="OpenNebula reconfiguration" + +depend() { + need one-context one-context-local + keyword -stop -shutdown +} + +start() { + ebegin "Starting one-context-reconfigure" + export TIMEOUT=0 + /usr/sbin/one-context-reconfigure + eend $? +} diff --git a/src/etc/init.d/one-context-reconfigure-delayed##apk b/src/etc/init.d/one-context-reconfigure-delayed##apk new file mode 100755 index 0000000..e651efa --- /dev/null +++ b/src/etc/init.d/one-context-reconfigure-delayed##apk @@ -0,0 +1,14 @@ +#!/sbin/openrc-run + +name="OpenNebula delayed reconfiguration" + +depend() { + need one-context one-context-local + keyword -stop -shutdown +} + +start() { + ebegin "Starting one-context-reconfigure-delayed" + /usr/sbin/one-context-reconfigure + eend $? +} diff --git a/src/etc/init.d/one-context-reconfigure-delayed##rpm.sysv.one b/src/etc/init.d/one-context-reconfigure-delayed##rpm.sysv.one index d562466..c61f3c6 100755 --- a/src/etc/init.d/one-context-reconfigure-delayed##rpm.sysv.one +++ b/src/etc/init.d/one-context-reconfigure-delayed##rpm.sysv.one @@ -35,7 +35,7 @@ case "$1" in start) - action $"Starting OpenNebula reconfiguration: " \ + action $"Starting OpenNebula delayed reconfiguration: " \ /usr/sbin/one-context-reconfigure ;; *) diff --git a/src/etc/one-context.d/loc-10-network##apk b/src/etc/one-context.d/loc-10-network##apk new file mode 100755 index 0000000..b307abf --- /dev/null +++ b/src/etc/one-context.d/loc-10-network##apk @@ -0,0 +1,257 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2010-2014, C12G Labs S.L. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +ACTION=$1 + +if [ -z "$ACTION" ]; then + ACTION="none" +fi + +# Gets IP address from a given MAC +mac2ip() { + mac=$1 + + let ip_a=0x`echo $mac | cut -d: -f 3` + let ip_b=0x`echo $mac | cut -d: -f 4` + let ip_c=0x`echo $mac | cut -d: -f 5` + let ip_d=0x`echo $mac | cut -d: -f 6` + + ip="$ip_a.$ip_b.$ip_c.$ip_d" + + echo $ip +} + +# Gets the network part of an IP +get_network() { + network=$(get_iface_var "NETWORK") + + if [ -z "$network" ]; then + network="$(echo $IP | cut -d'.' -f1,2,3).0" + fi + + echo $network +} + +# Gets the network mask +get_mask() { + mask=$(get_iface_var "MASK") + + if [ -z "$mask" ]; then + mask="255.255.255.0" + fi + + echo $mask +} + +# Gets device MTU +get_mtu() { + mtu=$(get_iface_var "MTU") + + echo $mtu +} + +is_gateway() { + if [ -z "$GATEWAY_IFACE_NUM" ]; then + true + else + [ "$IFACE_NUM" = "$GATEWAY_IFACE_NUM" ] + fi +} + +# Gets the network gateway +get_gateway() { + if is_gateway; then + get_iface_var "GATEWAY" + fi +} + +# Gets the network gateway6 +get_gateway6() { + if is_gateway; then + get_iface_var "GATEWAY6" + fi +} + +get_ip() { + ip=$(get_iface_var "IP") + + if [ -z "$ip" ]; then + ip=$(mac2ip $MAC) + fi + + echo $ip +} + +get_iface_var() { + var_name="${UPCASE_DEV}_$1" + var=$(eval "echo \"\${$var_name}\"") + + echo $var +} + +gen_iface_conf() { + cat < /etc/network/interfaces + #echo "source /etc/network/interfaces.d/*.cfg" >> /etc/network/interfaces +} + +deactivate_network() +{ + . /etc/os-release + if [ $ID = "ubuntu" ]; then + IFACES=`/sbin/ifquery --list -a` + + for i in $IFACES; do + if [ $i != 'lo' ]; then + /sbin/ifdown $i + /sbin/ip addr flush dev $i + fi + done + else + service networking stop + fi +} + +activate_network() +{ + . /etc/os-release + if [ $ID = "ubuntu" ]; then + IFACES=`/sbin/ifquery --list -a` + + for i in $IFACES; do + /sbin/ifup $i + done + else + service networking stop + sleep 1 + service networking start + fi + + sleep 2 +} + +[ $ACTION == "reconfigure" ] && deactivate_network +configure_network +[ $ACTION == "reconfigure" ] && activate_network + diff --git a/src/etc/one-context.d/loc-12-firewall##apk b/src/etc/one-context.d/loc-12-firewall##apk new file mode 100755 index 0000000..af0a279 --- /dev/null +++ b/src/etc/one-context.d/loc-12-firewall##apk @@ -0,0 +1,89 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2010-2016, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +get_management_interfaces() { + env | grep -E "^ETH[0-9]+_VROUTER_MANAGEMENT=YES" | sed 's/_.*$//' | tr 'ETH' 'eth' | sort +} + +get_context_interfaces() { + interfaces="$(env | grep -E "^ETH[0-9]+_MAC=" | sed 's/_.*$//' | tr 'ETH' 'eth' | sort)" + management="$(get_management_interfaces)" + if [ -n "$management" ]; then + interfaces="$(echo "$interfaces" | grep -v "$management")" + fi + echo "$interfaces" +} + +gen_header() { + cat < /etc/iptables/rules-save + +if [ "$1" == "reconfigure" ]; then + reload_service +else + rc-update add iptables boot +fi + diff --git a/src/etc/one-context.d/loc-15-ip_forward##apk b/src/etc/one-context.d/loc-15-ip_forward##apk new file mode 100755 index 0000000..7b87289 --- /dev/null +++ b/src/etc/one-context.d/loc-15-ip_forward##apk @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "${VROUTER_ID}${VROUTER_KEEPALIVED_ID}" ]; then + if [ -f /etc/sysctl.d/01-one.conf ]; then + unlink /etc/sysctl.d/01-one.conf + sysctl -w net.ipv4.ip_forward=0 + fi +else + echo 'net.ipv4.ip_forward = 1' >/etc/sysctl.d/01-one.conf + sysctl -w net.ipv4.ip_forward=1 +fi diff --git a/src/etc/one-context.d/loc-15-keepalived##apk b/src/etc/one-context.d/loc-15-keepalived##apk new file mode 100755 index 0000000..e63b950 --- /dev/null +++ b/src/etc/one-context.d/loc-15-keepalived##apk @@ -0,0 +1,144 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2010-2016, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +get_context_interfaces() { + env | grep -E "^ETH[0-9]+_VROUTER_IP=" | sed 's/_.*$//' | sort +} + +downcase() { + tr 'ETH' 'eth' +} + +get_ip() { + ip=$(get_iface_var "$1" "IP") + + echo $ip +} + +get_vrouter_id() { + vrouter_id="$VROUTER_KEEPALIVED_ID" + + if [ -z "$vrouter_id" ]; then + vrouter_id="0" + fi + + # vrouter_id is a byte + vrouter_id=$(( $vrouter_id & 255 )) + + if [ $? != 0 ]; then + vrouter_id="0" + fi + + echo $vrouter_id +} + +get_vrouter_password() { + password="$VROUTER_KEEPALIVED_PASSWORD" + + echo $password +} + +get_iface_var() { + var_name="$1_$2" + var=$(eval "echo \"\${$var_name}\"") + + echo $var +} + +gen_group() { + cat < /etc/keepalived/keepalived.conf + +if [ "$1" == "reconfigure" ]; then + reload_service +else + rc-update add keepalived boot +fi diff --git a/src/etc/one-context.d/net-99-report-ready b/src/etc/one-context.d/net-99-report-ready index fda424c..6f18a1b 100755 --- a/src/etc/one-context.d/net-99-report-ready +++ b/src/etc/one-context.d/net-99-report-ready @@ -1,9 +1,18 @@ #!/bin/bash +ENV_FILE=${ENV_FILE:-/var/run/one-context/one_env} + if [ "$REPORT_READY" != "YES" ]; then exit 0 fi +# $TOKENTXT is available only through the env. file +if [ -f "${ENV_FILE}" ]; then + . "${ENV_FILE}" +fi + +### + if which onegate >/dev/null 2>&1; then onegate vm update --data "READY=YES" diff --git a/src/etc/vmware-tools/scripts/poweroff-vm-default.d/poweroff##apk b/src/etc/vmware-tools/scripts/poweroff-vm-default.d/poweroff##apk new file mode 100755 index 0000000..2152a41 --- /dev/null +++ b/src/etc/vmware-tools/scripts/poweroff-vm-default.d/poweroff##apk @@ -0,0 +1,4 @@ +#!/bin/sh + +poweroff + diff --git a/src/lib/udev/rules.d/65-context.rules##apk b/src/lib/udev/rules.d/65-context.rules##apk new file mode 100644 index 0000000..1690355 --- /dev/null +++ b/src/lib/udev/rules.d/65-context.rules##apk @@ -0,0 +1,8 @@ +# On NIC hogplug the delayed reconfiguration is triggered. +SUBSYSTEM=="net", ACTION=="add", \ + RUN+="/sbin/service one-context-reconfigure-delayed restart" + +# On CONTEXT CD-ROM change the immediate reconfiguration is triggered. +SUBSYSTEM=="block", ACTION=="change", \ + ENV{ID_FS_TYPE}=="iso9660" ENV{ID_FS_LABEL_ENC}=="CONTEXT", \ + RUN+="/sbin/service one-context-reconfigure restart" diff --git a/src/usr/sbin/growpart##apk b/src/usr/sbin/growpart##apk new file mode 100755 index 0000000..d69bcf9 --- /dev/null +++ b/src/usr/sbin/growpart##apk @@ -0,0 +1,780 @@ +#!/bin/sh +# Copyright (C) 2011 Canonical Ltd. +# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. +# +# Authors: Scott Moser +# Juerg Haefliger +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# the fudge factor. if within this many bytes dont bother +FUDGE=${GROWPART_FUDGE:-$((1024*1024))} +TEMP_D="" +RESTORE_FUNC="" +RESTORE_HUMAN="" +VERBOSITY=0 +DISK="" +PART="" +PT_UPDATE=false +DRY_RUN=0 + +SFDISK_VERSION="" +SFDISK_2_26="22600" +SFDISK_V_WORKING_GPT="22603" +MBR_BACKUP="" +GPT_BACKUP="" +_capture="" + +error() { + echo "$@" 1>&2 +} + +fail() { + [ $# -eq 0 ] || echo "FAILED:" "$@" + exit 2 +} + +nochange() { + echo "NOCHANGE:" "$@" + exit 1 +} + +changed() { + echo "CHANGED:" "$@" + exit 0 +} + +change() { + echo "CHANGE:" "$@" + exit 0 +} + +cleanup() { + if [ -n "${RESTORE_FUNC}" ]; then + error "***** WARNING: Resize failed, attempting to revert ******" + if ${RESTORE_FUNC} ; then + error "***** Appears to have gone OK ****" + else + error "***** FAILED! ******" + if [ -n "${RESTORE_HUMAN}" -a -f "${RESTORE_HUMAN}" ]; then + error "**** original table looked like: ****" + cat "${RESTORE_HUMAN}" 1>&2 + else + error "We seem to have not saved the partition table!" + fi + fi + fi + [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" +} + +debug() { + local level=${1} + shift + [ "${level}" -gt "${VERBOSITY}" ] && return + if [ "${DEBUG_LOG}" ]; then + echo "$@" >>"${DEBUG_LOG}" + else + error "$@" + fi +} + +debugcat() { + local level="$1" + shift; + [ "${level}" -gt "$VERBOSITY" ] && return + if [ "${DEBUG_LOG}" ]; then + cat "$@" >>"${DEBUG_LOG}" + else + cat "$@" 1>&2 + fi +} + +mktemp_d() { + # just a mktemp -d that doens't need mktemp if its not there. + _RET=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX" 2>/dev/null) && + return + _RET=$(umask 077 && t="${TMPDIR:-/tmp}/${0##*/}.$$" && + mkdir "${t}" && echo "${t}") + return +} + +Usage() { + cat <&2 + error "$@" + exit 2 +} + +sfdisk_restore_legacy() { + sfdisk --no-reread "${DISK}" -I "${MBR_BACKUP}" +} + +sfdisk_restore() { + # files are named: sfdisk--.bak + local f="" offset="" fails=0 + for f in "${MBR_BACKUP}"*.bak; do + [ -f "$f" ] || continue + offset=${f##*-} + offset=${offset%.bak} + [ "$offset" = "$f" ] && { + error "WARN: confused by file $f"; + continue; + } + dd "if=$f" "of=${DISK}" seek=$(($offset)) bs=1 conv=notrunc || + { error "WARN: failed restore from $f"; fails=$(($fails+1)); } + done + return $fails +} + +sfdisk_worked_but_blkrrpart_failed() { + local ret="$1" output="$2" + # exit code found was just 1, but dont insist on that + #[ $ret -eq 1 ] || return 1 + # Successfully wrote the new partition table + grep -qi "Success.* wrote.* new.* partition" "$output" && + grep -qi "BLKRRPART: Device or resource busy" "$output" + return +} + +get_sfdisk_version() { + # set SFDISK_VERSION to MAJOR*10000+MINOR*100+MICRO + local out oifs="$IFS" ver="" + [ -n "$SFDISK_VERSION" ] && return 0 + # expected output: sfdisk from util-linux 2.25.2 + out=$(sfdisk --version) || + { error "failed to get sfdisk version"; return 1; } + set -- $out + ver=$4 + case "$ver" in + [0-9]*.[0-9]*.[0-9]|[0-9].[0-9]*) + IFS="."; set -- $ver; IFS="$oifs" + SFDISK_VERSION=$(($1*10000+$2*100+${3:-0})) + return 0;; + *) error "unexpected output in sfdisk --version [$out]" + return 1;; + esac +} + +resize_sfdisk() { + local humanpt="${TEMP_D}/recovery" + local mbr_backup="${TEMP_D}/orig.save" + local restore_func="" + local format="$1" + + local change_out=${TEMP_D}/change.out + local dump_out=${TEMP_D}/dump.out + local new_out=${TEMP_D}/new.out + local dump_mod=${TEMP_D}/dump.mod + local tmp="${TEMP_D}/tmp.out" + local err="${TEMP_D}/err.out" + local mbr_max_512="4294967296" + + local pt_start pt_size pt_end max_end new_size change_info dpart + local sector_num sector_size disk_size tot out + + rqe sfd_list sfdisk --list --unit=S "$DISK" >"$tmp" || + fail "failed: sfdisk --list $DISK" + if [ "${SFDISK_VERSION}" -lt ${SFDISK_2_26} ]; then + # exected output contains: Units: sectors of 512 bytes, ... + out=$(awk '$1 == "Units:" && $5 ~ /bytes/ { print $4 }' "$tmp") || + fail "failed to read sfdisk output" + if [ -z "$out" ]; then + error "WARN: sector size not found in sfdisk output, assuming 512" + sector_size=512 + else + sector_size="$out" + fi + local _w _cyl _w1 _heads _w2 sectors _w3 t s + # show-size is in units of 1024 bytes (same as /proc/partitions) + t=$(sfdisk --show-size "${DISK}") || + fail "failed: sfdisk --show-size $DISK" + disk_size=$((t*1024)) + sector_num=$(($disk_size/$sector_size)) + msg="disk size '$disk_size' not evenly div by sector size '$sector_size'" + [ "$((${disk_size}%${sector_size}))" -eq 0 ] || + error "WARN: $msg" + restore_func=sfdisk_restore_legacy + else + # --list first line output: + # Disk /dev/vda: 20 GiB, 21474836480 bytes, 41943040 sectors + local _x + read _x _x _x _x disk_size _x sector_num _x < "$tmp" + sector_size=$((disk_size/$sector_num)) + restore_func=sfdisk_restore + fi + + debug 1 "$sector_num sectors of $sector_size. total size=${disk_size} bytes" + [ $(($disk_size/512)) -gt $mbr_max_512 ] && + debug 1 "WARN: disk is larger than 2TB. additional space will go unused." + + rqe sfd_dump sfdisk --unit=S --dump "${DISK}" >"${dump_out}" || + fail "failed to dump sfdisk info for ${DISK}" + RESTORE_HUMAN="$dump_out" + + { + echo "## sfdisk --unit=S --dump ${DISK}" + cat "${dump_out}" + } >"$humanpt" + + [ $? -eq 0 ] || fail "failed to save sfdisk -d output" + RESTORE_HUMAN="$humanpt" + + debugcat 1 "$humanpt" + + sed -e 's/,//g; s/start=/start /; s/size=/size /' "${dump_out}" \ + >"${dump_mod}" || + fail "sed failed on dump output" + + dpart="${DISK}${PART}" # disk and partition number + if [ -b "${DISK}p${PART}" -a "${DISK%[0-9]}" != "${DISK}" ]; then + # for block devices that end in a number (/dev/nbd0) + # the partition is "p" (/dev/nbd0p1) + dpart="${DISK}p${PART}" + elif [ "${DISK#/dev/loop[0-9]}" != "${DISK}" ]; then + # for /dev/loop devices, sfdisk output will be p + # format also, even though there is not a device there. + dpart="${DISK}p${PART}" + fi + + pt_start=$(awk '$1 == pt { print $4 }' "pt=${dpart}" <"${dump_mod}") && + pt_size=$(awk '$1 == pt { print $6 }' "pt=${dpart}" <"${dump_mod}") && + [ -n "${pt_start}" -a -n "${pt_size}" ] && + pt_end=$((${pt_size}+${pt_start})) || + fail "failed to get start and end for ${dpart} in ${DISK}" + + # find the minimal starting location that is >= pt_end + max_end=$(awk '$3 == "start" { if($4 >= pt_end && $4 < min) + { min = $4 } } END { printf("%s\n",min); }' \ + min=${sector_num} pt_end=${pt_end} "${dump_mod}") && + [ -n "${max_end}" ] || + fail "failed to get max_end for partition ${PART}" + + mbr_max_sectors=$((mbr_max_512*$((sector_size/512)))) + if [ "$max_end" -gt "$mbr_max_sectors" ]; then + max_end=$mbr_max_sectors + fi + + if [ "$format" = "gpt" ]; then + # sfdisk respects 'last-lba' in input, and complains about + # partitions that go past that. without it, it does the right thing. + sed -i '/^last-lba:/d' "$dump_out" || + fail "failed to remove last-lba from output" + fi + + local gpt_second_size="33" + if [ "${max_end}" -gt "$((${sector_num}-${gpt_second_size}))" ]; then + # if mbr allow subsequent conversion to gpt without shrinking the + # partition. safety net at cost of 33 sectors, seems reasonable. + # if gpt, we can't write there anyway. + debug 1 "padding ${gpt_second_size} sectors for gpt secondary header" + max_end=$((${sector_num}-${gpt_second_size})) + fi + + debug 1 "max_end=${max_end} tot=${sector_num} pt_end=${pt_end}" \ + "pt_start=${pt_start} pt_size=${pt_size}" + [ $((${pt_end})) -eq ${max_end} ] && + nochange "partition ${PART} is size ${pt_size}. it cannot be grown" + [ $((${pt_end}+(${FUDGE}/$sector_size))) -gt ${max_end} ] && + nochange "partition ${PART} could only be grown by" \ + "$((${max_end}-${pt_end})) [fudge=$((${FUDGE}/$sector_size))]" + + # now, change the size for this partition in ${dump_out} to be the + # new size + new_size=$((${max_end}-${pt_start})) + sed "\|^\s*${dpart} |s/${pt_size},/${new_size},/" "${dump_out}" \ + >"${new_out}" || + fail "failed to change size in output" + + change_info="partition=${PART} start=${pt_start} old: size=${pt_size} end=${pt_end} new: size=${new_size},end=${max_end}" + if [ ${DRY_RUN} -ne 0 ]; then + echo "CHANGE: ${change_info}" + { + echo "# === old sfdisk -d ===" + cat "${dump_out}" + echo "# === new sfdisk -d ===" + cat "${new_out}" + } 1>&2 + exit 0 + fi + + MBR_BACKUP="${mbr_backup}" + LANG=C sfdisk --no-reread "${DISK}" --force \ + -O "${mbr_backup}" <"${new_out}" >"${change_out}" 2>&1 + ret=$? + [ $ret -eq 0 ] || RESTORE_FUNC="${restore_func}" + + if [ $ret -eq 0 ]; then + : + elif $PT_UPDATE && + sfdisk_worked_but_blkrrpart_failed "$ret" "${change_out}"; then + # if the command failed, but it looks like only because + # the device was busy and we have pt_update, then go on + debug 1 "sfdisk failed, but likely only because of blkrrpart" + else + error "attempt to resize ${DISK} failed. sfdisk output below:" + sed 's,^,| ,' "${change_out}" 1>&2 + fail "failed to resize" + fi + + rq pt_update pt_update "$DISK" "$PART" || + fail "pt_resize failed" + + RESTORE_FUNC="" + + changed "${change_info}" + + # dump_out looks something like: + ## partition table of /tmp/out.img + #unit: sectors + # + #/tmp/out.img1 : start= 1, size= 48194, Id=83 + #/tmp/out.img2 : start= 48195, size= 963900, Id=83 + #/tmp/out.img3 : start= 1012095, size= 305235, Id=82 + #/tmp/out.img4 : start= 1317330, size= 771120, Id= 5 + #/tmp/out.img5 : start= 1317331, size= 642599, Id=83 + #/tmp/out.img6 : start= 1959931, size= 48194, Id=83 + #/tmp/out.img7 : start= 2008126, size= 80324, Id=83 +} + +gpt_restore() { + sgdisk -l "${GPT_BACKUP}" "${DISK}" +} + +resize_sgdisk() { + GPT_BACKUP="${TEMP_D}/pt.backup" + + local pt_info="${TEMP_D}/pt.info" + local pt_pretend="${TEMP_D}/pt.pretend" + local pt_data="${TEMP_D}/pt.data" + local out="${TEMP_D}/out" + + local dev="disk=${DISK} partition=${PART}" + + local pt_start pt_end pt_size last pt_max code guid name new_size + local old new change_info sector_size + + # Dump the original partition information and details to disk. This is + # used in case something goes wrong and human interaction is required + # to revert any changes. + rqe sgd_info sgdisk "--info=${PART}" --print "${DISK}" >"${pt_info}" || + fail "${dev}: failed to dump original sgdisk info" + RESTORE_HUMAN="${pt_info}" + + sector_size=$(awk '$0 ~ /^Logical sector size:.*bytes/ { print $4 }' \ + "$pt_info") && [ -n "$sector_size" ] || { + sector_size=512 + error "WARN: did not find sector size, assuming 512" + } + + debug 1 "$dev: original sgdisk info:" + debugcat 1 "${pt_info}" + + # Pretend to move the backup GPT header to the end of the disk and dump + # the resulting partition information. We use this info to determine if + # we have to resize the partition. + rqe sgd_pretend sgdisk --pretend --move-second-header \ + --print "${DISK}" >"${pt_pretend}" || + fail "${dev}: failed to dump pretend sgdisk info" + + debug 1 "$dev: pretend sgdisk info" + debugcat 1 "${pt_pretend}" + + # Extract the partition data from the pretend dump + awk 'found { print } ; $1 == "Number" { found = 1 }' \ + "${pt_pretend}" >"${pt_data}" || + fail "${dev}: failed to parse pretend sgdisk info" + + # Get the start and end sectors of the partition to be grown + pt_start=$(awk '$1 == '"${PART}"' { print $2 }' "${pt_data}") && + [ -n "${pt_start}" ] || + fail "${dev}: failed to get start sector" + pt_end=$(awk '$1 == '"${PART}"' { print $3 }' "${pt_data}") && + [ -n "${pt_end}" ] || + fail "${dev}: failed to get end sector" + pt_size="$((${pt_end} - ${pt_start}))" + + # Get the last usable sector + last=$(awk '/last usable sector is/ { print $NF }' \ + "${pt_pretend}") && [ -n "${last}" ] || + fail "${dev}: failed to get last usable sector" + + # Find the minimal start sector that is >= pt_end + pt_max=$(awk '{ if ($2 >= pt_end && $2 < min) { min = $2 } } END \ + { print min }' min="${last}" pt_end="${pt_end}" \ + "${pt_data}") && [ -n "${pt_max}" ] || + fail "${dev}: failed to find max end sector" + + debug 1 "${dev}: pt_start=${pt_start} pt_end=${pt_end}" \ + "pt_size=${pt_size} pt_max=${pt_max} last=${last}" + + # Check if the partition can be grown + [ "${pt_end}" -eq "${pt_max}" ] && + nochange "${dev}: size=${pt_size}, it cannot be grown" + [ "$((${pt_end} + ${FUDGE}/${sector_size}))" -gt "${pt_max}" ] && + nochange "${dev}: could only be grown by" \ + "$((${pt_max} - ${pt_end})) [fudge=$((${FUDGE}/$sector_size))]" + + # The partition can be grown if we made it here. Get some more info + # about it so we can do it properly. + # FIXME: Do we care about the attribute flags? + code=$(awk '/^Partition GUID code:/ { print $4 }' "${pt_info}") + guid=$(awk '/^Partition unique GUID:/ { print $4 }' "${pt_info}") + name=$(awk '/^Partition name:/ { gsub(/'"'"'/, "") ; \ + if (NF >= 3) print substr($0, index($0, $3)) }' "${pt_info}") + [ -n "${code}" -a -n "${guid}" ] || + fail "${dev}: failed to parse sgdisk details" + + debug 1 "${dev}: code=${code} guid=${guid} name='${name}'" + local wouldrun="" + [ "$DRY_RUN" -ne 0 ] && wouldrun="would-run" + + # Calculate the new size of the partition + new_size=$((${pt_max} - ${pt_start})) + old="old: size=${pt_size},end=${pt_end}" + new="new: size=${new_size},end=${pt_max}" + change_info="${dev}: start=${pt_start} ${old} ${new}" + + # Backup the current partition table, we're about to modify it + rq sgd_backup $wouldrun sgdisk "--backup=${GPT_BACKUP}" "${DISK}" || + fail "${dev}: failed to backup the partition table" + + # Modify the partition table. We do it all in one go (the order is + # important!): + # - move the GPT backup header to the end of the disk + # - delete the partition + # - recreate the partition with the new size + # - set the partition code + # - set the partition GUID + # - set the partition name + rq sgdisk_mod $wouldrun sgdisk --move-second-header "--delete=${PART}" \ + "--new=${PART}:${pt_start}:${pt_max}" \ + "--typecode=${PART}:${code}" \ + "--partition-guid=${PART}:${guid}" \ + "--change-name=${PART}:${name}" "${DISK}" && + rq pt_update $wouldrun pt_update "$DISK" "$PART" || { + RESTORE_FUNC=gpt_restore + fail "${dev}: failed to repartition" + } + + # Dry run + [ "${DRY_RUN}" -ne 0 ] && change "${change_info}" + + changed "${change_info}" +} + +kver_to_num() { + local kver="$1" maj="" min="" mic="0" + kver=${kver%%-*} + maj=${kver%%.*} + min=${kver#${maj}.} + min=${min%%.*} + mic=${kver#${maj}.${min}.} + [ "$kver" = "$mic" ] && mic=0 + _RET=$(($maj*1000*1000+$min*1000+$mic)) +} + +kver_cmp() { + local op="$2" n1="" n2="" + kver_to_num "$1" + n1="$_RET" + kver_to_num "$3" + n2="$_RET" + [ $n1 $op $n2 ] +} + +rq() { + # runquieterror(label, command) + # gobble stderr of a command unless it errors + local label="$1" ret="" efile="" + efile="$TEMP_D/$label.err" + shift; + + local rlabel="running" + [ "$1" = "would-run" ] && rlabel="would-run" && shift + + local cmd="" x="" + for x in "$@"; do + [ "${x#* }" != "$x" -o "${x#* \"}" != "$x" ] && x="'$x'" + cmd="$cmd $x" + done + cmd=${cmd# } + + debug 2 "$rlabel[$label][$_capture]" "$cmd" + [ "$rlabel" = "would-run" ] && return 0 + + if [ "${_capture}" = "erronly" ]; then + "$@" 2>"$TEMP_D/$label.err" + ret=$? + else + "$@" >"$TEMP_D/$label.err" 2>&1 + ret=$? + fi + if [ $ret -ne 0 ]; then + error "failed [$label:$ret]" "$@" + cat "$efile" 1>&2 + fi + return $ret +} + +rqe() { + local _capture="erronly" + rq "$@" +} + +verify_ptupdate() { + local input="$1" found="" reason="" kver="" + + # we can always satisfy 'off' + if [ "$input" = "off" ]; then + _RET="false"; + return 0; + fi + + if command -v partx >/dev/null 2>&1; then + local out="" ret=0 + out=$(partx --help 2>&1) + ret=$? + if [ $ret -eq 0 ]; then + echo "$out" | grep -q -- --update || { + reason="partx has no '--update' flag in usage." + found="off" + } + else + reason="'partx --help' returned $ret. assuming it is old." + found="off" + fi + else + reason="no 'partx' command" + found="off" + fi + + if [ -z "$found" ]; then + if [ "$(uname)" != "Linux" ]; then + reason="Kernel is not Linux per uname." + found="off" + fi + fi + + if [ -z "$found" ]; then + kver=$(uname -r) || debug 1 "uname -r failed!" + + if ! kver_cmp "${kver-0.0.0}" -ge 3.8.0; then + reason="Kernel '$kver' < 3.8.0." + found="off" + fi + fi + + if [ -z "$found" ]; then + _RET="true" + return 0 + fi + + case "$input" in + on) error "$reason"; return 1;; + auto) + _RET="false"; + debug 1 "partition update disabled: $reason" + return 0;; + force) + _RET="true" + error "WARNING: ptupdate forced on even though: $reason" + return 0;; + esac + error "unknown input '$input'"; + return 1; +} + +pt_update() { + local dev="$1" part="$2" update="${3:-$PT_UPDATE}" + if ! $update; then + return 0 + fi + # partx only works on block devices (do not run on file) + [ -b "$dev" ] || return 0 + partx --update "$part" "$dev" +} + +has_cmd() { + command -v "${1}" >/dev/null 2>&1 +} + +resize_sgdisk_gpt() { + resize_sgdisk gpt +} + +resize_sgdisk_dos() { + fail "unable to resize dos label with sgdisk" +} + +resize_sfdisk_gpt() { + resize_sfdisk gpt +} + +resize_sfdisk_dos() { + resize_sfdisk dos +} + +get_table_format() { + local out="" disk="$1" + if has_cmd blkid && out=$(blkid -o value -s PTTYPE "$disk") && + [ "$out" = "dos" -o "$out" = "gpt" ]; then + _RET="$out" + return + fi + _RET="dos" + if [ ${SFDISK_VERSION} -lt ${SFDISK_2_26} ] && + out=$(sfdisk --id --force "$disk" 1 2>/dev/null); then + if [ "$out" = "ee" ]; then + _RET="gpt" + else + _RET="dos" + fi + return + elif out=$(LANG=C sfdisk --list "$disk"); then + out=$(echo "$out" | sed -e '/Disklabel type/!d' -e 's/.*: //') + case "$out" in + gpt|dos) _RET="$out";; + *) error "WARN: unknown label $out";; + esac + fi +} + +get_resizer() { + local format="$1" user=${2:-"auto"} + + case "$user" in + sgdisk) _RET="resize_sgdisk_$format"; return;; + sfdisk) _RET="resize_sfdisk_$format"; return;; + auto) :;; + *) error "unexpected input: '$user'";; + esac + + if [ "$format" = "dos" ]; then + _RET="resize_sfdisk_dos" + return 0 + fi + + if [ "${SFDISK_VERSION}" -ge ${SFDISK_V_WORKING_GPT} ]; then + # sfdisk 2.26.2 works for resize but loses type (LP: #1474090) + _RET="resize_sfdisk_gpt" + elif has_cmd sgdisk; then + _RET="resize_sgdisk_$format" + else + error "no tools available to resize disk with '$format'" + return 1 + fi + return 0 +} + +pt_update="auto" +resizer=${GROWPART_RESIZER:-"auto"} +while [ $# -ne 0 ]; do + cur=${1} + next=${2} + case "$cur" in + -h|--help) + Usage + exit 0 + ;; + --fudge) + FUDGE=${next} + shift + ;; + -N|--dry-run) + DRY_RUN=1 + ;; + -u|--update|--update=*) + if [ "${cur#--update=}" != "$cur" ]; then + next="${cur#--update=}" + else + shift + fi + case "$next" in + off|auto|force|on) pt_update=$next;; + *) fail "unknown --update option: $next";; + esac + ;; + -v|--verbose) + VERBOSITY=$(($VERBOSITY+1)) + ;; + --) + shift + break + ;; + -*) + fail "unknown option ${cur}" + ;; + *) + if [ -z "${DISK}" ]; then + DISK=${cur} + else + [ -z "${PART}" ] || fail "confused by arg ${cur}" + PART=${cur} + fi + ;; + esac + shift +done + +[ -n "${DISK}" ] || bad_Usage "must supply disk and partition-number" +[ -n "${PART}" ] || bad_Usage "must supply partition-number" + +has_cmd "sfdisk" || fail "sfdisk not found" +get_sfdisk_version || fail + +[ -e "${DISK}" ] || fail "${DISK}: does not exist" + +[ "${PART#*[!0-9]}" = "${PART}" ] || fail "partition-number must be a number" + +verify_ptupdate "$pt_update" || fail +PT_UPDATE=$_RET + +debug 1 "update-partition set to $PT_UPDATE" + +mktemp_d && TEMP_D="${_RET}" || fail "failed to make temp dir" +trap cleanup 0 # EXIT - some shells may not like 'EXIT' but are ok with 0 + +# get the ID of the first partition to determine if it's MBR or GPT +get_table_format "$DISK" || fail +format=$_RET +get_resizer "$format" "$resizer" || + fail "failed to get a resizer for id '$id'" +resizer=$_RET + +debug 1 "resizing $PART on $DISK using $resizer" +"$resizer" + +# vi: ts=4 noexpandtab diff --git a/src/usr/sbin/one-contextd b/src/usr/sbin/one-contextd index b2cf0c5..4cc8cbf 100755 --- a/src/usr/sbin/one-contextd +++ b/src/usr/sbin/one-contextd @@ -28,7 +28,7 @@ CONTEXT_BASE="${RUNTIME_DIR}/context.sh" SYSLOG_TAG="$(basename $0)" [ -d "${RUNTIME_DIR}" ] || mkdir -m 0700 -p "${RUNTIME_DIR}" -CONTEXT_NEW=$(mktemp "${CONTEXT_BASE}.XXXX" 2>/dev/null) +CONTEXT_NEW=$(mktemp "${CONTEXT_BASE}.XXXXXX" 2>/dev/null) SYSLOG_FACILITY="${SYSLOG_FACILITY:-local3}" shopt -s extglob @@ -140,7 +140,7 @@ function vmware_context { function get_new_context { local dev_context=$(blkid -l -t LABEL="CONTEXT" -o device) if [ -e "${dev_context}" ]; then - MOUNT_DIR=$(mktemp -d "${RUNTIME_DIR}/mount.XXXX" 2>/dev/null) + MOUNT_DIR=$(mktemp -d "${RUNTIME_DIR}/mount.XXXXXX" 2>/dev/null) if ! [ -d "${MOUNT_DIR}" ]; then log err 'Error: Failed to create mountpoint' 2 exit 1 diff --git a/targets.sh b/targets.sh index 1b7cfdb..70ee879 100644 --- a/targets.sh +++ b/targets.sh @@ -77,6 +77,18 @@ case "${TARGET}" in PREUN=${PREUN:-preuninstall.ec2} ;; + + 'alpine') + NAME=${NAME:-one-context} + RELSUFFIX=${RELSUFFIX:-} + TYPE=${TYPE:-apk} + TAGS=${TAGS:-apk one} + DEPENDS=${DEPENDS:-util-linux bash curl rsync udev iptables sfdisk e2fsprogs-extra open-vm-tools qemu-guest-agent keepalived quagga} + REPLACES=${REPLACES:-} + POSTIN=${POSTINST:-postinstall.one} + PREUN=${PREUN:-preuninstall.one} + ;; + 'arch') NAME=${NAME:-one-context} TYPE=${TYPE:-dir}