[BACK]Return to update_openbsd CVS log [TXT][DIR] Up to [local] / openbsd / update_openbsd

File: [local] / openbsd / update_openbsd / update_openbsd (download)

Revision 1.65, Sat Feb 1 19:13:53 2014 UTC (10 years, 3 months ago) by andrew
Branch: MAIN
Changes since 1.64: +24 -31 lines

Require running update_openbsd as root

It doesn't make sense to use SUDO to run specific bits because it is flaky.
That is, sometimes the sudo timeout will happen before the next step so you
will have to notice and type the password again.  This is often a problem
on machines with slower disks.

It does mean downloading and verifying the sets as root instead of an
normal user but that is not too big of a deal.

It might be worthwhile to wrap the privileged actions in a function and figure
out how to run that entire function as root but that's complication we don't
actually need.

Thanks to Chris Cappuccio for the suggestion.

#!/bin/sh
# $AFresh1: update_openbsd,v 1.65 2014/02/01 19:13:53 andrew Exp $
# 
# Copyright (c) 2012 Andrew Fresh <andrew@afresh1.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

installed_sets() {
    local misc=/usr/share/doc/README
    local man=/usr/share/man/man1/intro.1
    local comp=/usr/bin/cc
    local game=/usr/games/
    local xbase=/usr/X11R6/
    local xetc=/etc/X11/xinit/xinitrc
    local xfont=/usr/X11R6/lib/X11/fonts
    local xserv=/usr/X11R6/bin/X
    local xshare=/usr/X11R6/bin/startx

    local _c _d _e
    echo -n base
    echo -n ' etc'
    for _d in misc man comp game xbase xetc xfont xserv xshare; do
        eval _e=\$${_d}
        _c=`ls $_e 2> /dev/null | wc -l`
        #echo $_c $_d $_e
        if [ $_c -ne 0 ]; then
            echo -n " $_d"
        fi
    done

    sendmail -d0.1 --badoption </dev/null 2>/dev/null | grep -q SASL
    if [ $? == 0 ]; then 
        echo -n ' sendmail-smtp_auth'
    fi
}

kernel_file_version() {
    echo exit | config -e $1 | grep -A1 ^OpenBSD
    #what $1 | sed -ne 's/[[:blank:]]\{1,\}//p'
}

version_in() {
        local _proto=${FTP%%://*}
        local _file

        if [ X"ftp" == X"${_proto}" ]; then
            local _list=`echo "ls base*.tgz" | ${FTP_CMD} ${FTP}/`
            _file=`echo ${_list} | awk '/base[0-9][0-9].tgz/ { print $9 }'`

        elif [ X"http" == X"${_proto}" ]; then
            local _list=`${FTP_CMD} -V -o - ${FTP}/`
            _file=`echo ${_list} | awk '/[^x]base[0-9][0-9]*\.tgz/ { 
                    sub("^.*base","base");
                    sub("\.tgz.*",".tgz"); 
                    print $0;
                }'`

        elif [ X"scp" == X"${_proto}" ]; then
            echo SCP is not yet supported >&2
            return 2

        else 
            echo Unsupported FTP ${FTP} >&2
            return 2

        fi

        local _v=${_file##*base}
        _v=${_v%.tgz*}
        echo $_v
}

set_version() {
    CUR_VER=`uname -r`
    NEW_VER=`dc -e "$CUR_VER 0.1 + p"`
    FILE_VER=""
    FTP=""

    local _cv=`echo $CUR_VER | sed -e 's/\.//'`
    local _nv=`echo $NEW_VER | sed -e 's/\.//'`
    local _v

    if [ X"No" != X"$FORCE_DIR" -a -d $FORCE_DIR ]; then
        _dir=$FORCE_DIR
        if [ -e ${_dir}/base${_nv}.tgz ]; then
            _v=$_nv
        elif [ -e ${_dir}/base${_cv}.tgz ]; then
            NEW_VER=$CUR_VER
            _v=$_cv
        fi

    elif [ -d $CUR_VER ]; then
        _dir=$CUR_VER
        NEW_VER=$CUR_VER
        if [ -e ${_dir}/base${_cv}.tgz ]; then
            _v=$_cv
        fi

    elif [ -d $NEW_VER ]; then
        _dir=$NEW_VER
        if [ -e ${_dir}/base${_nv}.tgz ]; then
            _v=$_nv
        fi

    fi

    if [ X"" != X"${MIRROR}" -a X"" == X"${_v}" ]; then
        if [ X"No" == X"${FORCE_DIR}" ]; then
            _dir=${NEW_VER}
        else
            _dir=${FORCE_DIR}
        fi
        FTP=${MIRROR}/${_dir}/`machine`

        _v=`version_in`

        if [ X"" == X"${_v}" ]; then
            if [ X"No" != X"$FORCE_DIR" ]; then
                echo No sets in forced [${FTP}] >&2
                return 2
            fi

            NEW_VER=$CUR_VER
            _dir=${NEW_VER}
            FTP=${MIRROR}/${_dir}/`machine`

            _v=`version_in`
        fi

        if [ X"" == X"${_v}" ]; then
            echo No sets in [${FTP}] >&2
            return 2 
        elif [ X"${_cv}" == X"${_v}" ]; then
            NEW_VER=$CUR_VER
        elif [ X"${_nv}" == X"${_v}" ]; then
            NEW_VER=$NEW_VER
        else
            echo Invalid version [$_v] >&2
            return 2
        fi

        if [ X"No" == X"$FORCE_DIR" ]; then
            _dir=$NEW_VER
        fi

    fi

    if [ X"" == X"${_v}" ]; then
        if [ X"" == X"${MIRROR}" ]; then
            echo ERROR: No sets, and no MIRROR, unable to continue. >&2
        else
            echo ERROR: Unable to determine FILE_VER, check your MIRROR. >&2
        fi
        return 1
    fi

    if [ X"" == X"$RELEASEDIR" ]; then
        RELEASEDIR=`pwd`/$_dir
    fi

    FILE_VER=$_v
    if [ X"" != X"${MIRROR}" ]; then
        FTP=${MIRROR}/${_dir}/`machine`
    fi

    BOOT_KERNEL=`( \
        echo bsd; \
        [ -e /boot.conf ] && sed -E '/^ *(set +image|boot) +/!d ; \
            s///; s/^.*://; s/ .*$//' /boot.conf \
    ) | tail -1`
    BOOT_KERNEL=`follow_symlink /$BOOT_KERNEL`
    BOOT_KERNEL="/${BOOT_KERNEL#/}"

    BOOT_KERNEL_VERSION=`kernel_file_version $BOOT_KERNEL`

    BOOTED_KERNEL_VERSION=`sysctl -n kern.version`
    NEW_KERNEL_VERSION=""

    # We want to default to what we had
    INSTALL_KERNELS="${BOOT_KERNEL#/}"
    # if the boot kernel was our specially named bsd.sp, we install from bsd
    if [ X"$INSTALL_KERNELS" == X"bsd.sp" ]; then
        INSTALL_KERNELS="bsd"
    fi
    # with a second option of an mp kernel if is is a likely candidate
    if [ X"$INSTALL_KERNELS" != X"bsd.mp" ]; then
        local _ncpu=$(sysctl -n hw.ncpufound)
        [ $_ncpu -gt 1 ] && INSTALL_KERNELS="$INSTALL_KERNELS bsd.mp"
    fi
    # or just bsd otherwise
    if [ X"${INSTALL_KERNELS% *}" != X"bsd" ]; then
        INSTALL_KERNELS="$INSTALL_KERNELS bsd"
    fi
    BOOT_KERNELS=$INSTALL_KERNELS
    INSTALL_KERNELS="$INSTALL_KERNELS bsd.rd"
}

get_sets() {
    echo '==> GETTING SETS'
    if [ X"" == X"$FTP" ]; then
        echo ERROR: No FTP site set! >&2
        return 1
    fi

    mkdir -p ${RELEASEDIR}
    cd $RELEASEDIR

    local _v=$FILE_VER

    for _b in $INSTALL_KERNELS; do
        if [ ! -e ./${_b} ]; then
            echo "===> $FTP_CMD ${FTP}/${_b}"
            $FTP_CMD ${FTP}/${_b}
        fi
    done

    for _s in $INSTALLED_SETS; do
        local _file=${_s}${_v}.tgz
        if [ ${_s} == sendmail-smtp_auth ]; then
            _file=${_s}.gz
        fi

        if [ ! -e ./${_file} ]; then
            echo "===> $FTP_CMD ${FTP}/${_file}"
            $FTP_CMD ${FTP}/${_file}
        fi
    done

    local _type
    local _ftp
    for _type in $CHECKSUM_TYPES; do
        [ -e $_type ] && break
        _ftp=`echo "$FTP" | sed -e 's,://[^/]*/,://ftp.openbsd.org/,'`
        echo "===> $FTP_CMD ${_ftp}/$_type"
        $FTP_CMD ${_ftp}/$_type
    done
}

follow_symlink () {
    local _file=$1
    # This could go circular, but I dunno how to fix that.
    if [ -h $_file ]; then
        follow_symlink $( file $_file |
            grep 'symbolic link' | 
            sed -e s/^.*\\\`// -e s/\\\'\$// )
    else
        echo $_file
    fi
}

check_sum () {
    local _type=$1
    echo "==> CHECKING $_type SUMS"
    cd $RELEASEDIR

    if [ ! -e $_type ]; then
        echo $_type File does not exist!
        return 1
    fi

    local _nv=`echo $NEW_VER | sed -e 's/\.//'`
    local _signify=`which signify 2>/dev/null`
    local _keyfile=/etc/signify/${_nv}base.pub 
    local _b _s

    (
        for _b in $INSTALL_KERNELS; do echo "($_b)"        ; done
        for _s in $INSTALLED_SETS;  do echo "($_s$_nv.tgz)"; done
    ) > index
    

    if [ -n "$_signify" -a "$_type" != "${_type%.sig}" ]; then
        echo "===> Checking signature";
        if [ ! -e $_keyfile ]; then
            echo "key [$_keyfile] does not exist, cannot check $_type" >&2
            return 2
        fi
        signify -V -e -p $_keyfile -x $_type -m - | grep -f index | sum -c -
    else
       grep -f index $_type | sum -c
    fi

    if [ $? -ne 0 ]; then
        echo ERROR: $_type does not match! >&2
        return 1
    fi
}

check_sets() {
    echo '==> CHECKING SETS'
    cd $RELEASEDIR

    local _missing_sets
    local _v=$FILE_VER

    for _n in $INSTALL_KERNELS; do
        local _o=$_n
        [ X"bsd" == X"${_o}" -a -e /bsd.sp ] && _o=bsd.sp
        if [ -e /${_o} -a ! -e ./${_n} ]; then
            echo ${_o} does not exist
            _missing_sets=1
        fi

        if [ X"${BOOT_KERNEL}" == X"/${_o}" -a -e ./${_n} ]; then
            NEW_KERNEL_VERSION=`kernel_file_version ./${_n}`
        fi
    done

    if [ X"$NEW_KERNEL_VERSION" == X"" ]; then
        echo Missing replacement for boot kernel $BOOT_KERNEL >&2
        _missing_sets=1
    fi

    for _s in $INSTALLED_SETS; do
        local _file=${_s}${_v}.tgz
        if [ ${_s} == sendmail-smtp_auth ]; then
            _file=${_s}.gz
        fi
        if [ ! -e ./${_file} ]; then
            echo ${_file} does not exist
            _missing_sets=1
        fi
    done

    if [ X"" == X"${_missing_sets}" ]; then
        echo '===> All OK'
    fi

    local _type
    for _type in $CHECKSUM_TYPES; do
        if [ -e $_type ]; then
            check_sum $_type && break
            [ -z "$IGNORE_CHECKSUM_ERROR" ] && exit 1
        fi
    done

    return 0
}


install_kernels() {
    echo '==> INSTALLING KERNEL'

    if [ X"" == X"$RELEASEDIR" ]; then
        echo ERROR: no source for new kernels! >&2
        exit 1
    fi

    if [ X"$BOOT_KERNEL_VERSION" != X"$NEW_KERNEL_VERSION" ]; then
        echo "===> Backing up $BOOT_KERNEL to /obsd"
        ln -f $BOOT_KERNEL /obsd
        if [ $? -ne 0 ]; then
            echo "Error copying old kernel!" >&2
            exit 1
        fi
    fi

    cd $RELEASEDIR

    for _b in $INSTALL_KERNELS; do
        rm -f /nbsd
        local _bd=$_b
        [ X"${_b}" == X"bsd" ] && _bd="bsd.sp"

        local _is_boot=""
        [ X"$BOOT_KERNEL" == X"/${_bd}" ] && _is_boot="# boot kernel"
        
        echo "===> Copying $_b to /$_bd $_is_boot"
        cp ${_b} /nbsd && mv /nbsd /${_bd}
        if [ $? -ne 0 ]; then
            echo ERROR: Could not copy new $_bd kernel! >&2
            exit 1
        fi
    done

    cd $OLDPWD

    if [ ! -h /bsd ]; then
	    cd /
        for _b in $BOOT_KERNELS; do
            [ X"$_b" == X"bsd" ] && _b="bsd.sp"
            if [ -e $_b ]; then
                echo "===> symlinking $_b to /bsd"
                ln -sf $_b bsd
                if [ $? -ne 0 ]; then
                    echo ERROR: Could not symlink new kernel! >&2
                    exit 1
                fi
                break
            fi
        done
	    cd $OLDPWD
    fi
}

install_sets() {
    echo '==> INSTALLING SETS'

    if [ X"" == X"$RELEASEDIR" ]; then
        echo ERROR: no source for sets! >&2
        exit 1
    else
        cd $RELEASEDIR
    fi

    local _v=$FILE_VER

    local _sets=`ls *${_v}.tgz | grep -v ^base `
    for _f in ${_sets} base${_v}.tgz; do
        _path=$DESTDIR
        if [ X"etc${_v}.tgz"  == X"$_f" \
            -o X"xetc${_v}.tgz" == X"$_f" ]; then
            [ X"" != X"$SYSMERGE" ] && continue
            _path=/var/tmp/temproot
        fi

        echo "===> Extracting $_f to $_path"
        mkdir -p $_path
        tar -C $_path -xzphf ${RELEASEDIR}/${_f}
        if [ $? -ne 0 ]; then
            echo ERROR: Could not extract ${_f}! >&2
            exit 1
        fi
    done

    echo '===> Extracted all sets.'
}

install_sendmail_smtp_auth() {
    if [ -e ${RELEASEDIR}/sendmail-smtp_auth.gz ]; then
        gzcat ${RELEASEDIR}/sendmail-smtp_auth.gz > \
            ${RELEASEDIR}/sendmail-smtp_auth
    fi
    if [ -e ${RELEASEDIR}/sendmail-smtp_auth ]; then
        if ! pkg_info -qe 'cyrus-sasl-*'; then
            pkg_add -i cyrus-sasl
        fi

        install -o root -g smmsp -m 2555 \
            ${RELEASEDIR}/sendmail-smtp_auth \
            /usr/libexec/sendmail/sendmail

        echo '===> Installed sendmail with smtp_auth'
    fi
}

update_etc() {
    echo '==> UPDATING ETC'
    if [ ! -e $SYSMERGE ]; then
        echo "ERROR: Can't find sysmerge!" >&2
        exit 1;
    fi

    if [ X"" == X"$RELEASEDIR" ]; then
        echo "ERROR: no source for etc!" >&2
        exit 1
    fi

    cd $RELEASEDIR

    local _v=$FILE_VER
    local _args=""
    if [ -e etc${_v}.tgz ]; then
        _args="$_args -s ${RELEASEDIR}/etc${_v}.tgz"
    fi
    if [ -e xetc${_v}.tgz ]; then
        _args="$_args -x ${RELEASEDIR}/xetc${_v}.tgz"
    fi
    if [ X"" == X"$_args" ]; then 
        echo ERROR: No upgrade sets found! >&2
    else
        echo '==> RUNNING SYSMERGE'
        $SYSMERGE $_args
    fi

    cd $OLDPWD
}


if [ $(id -u) != 0 ]; then
    echo 'ERROR: need root privileges to run this script' >&2
    exit 1
fi

if [ -e /etc/update_openbsd.conf ]; then
    . /etc/update_openbsd.conf
fi

if [ -e ${HOME}/.update_openbsdrc ]; then
    . ${HOME}/.update_openbsdrc
fi

#MIRROR=${MIRROR:=ftp://ftp.openbsd.org/pub/OpenBSD}
FTP_CMD=${FTP_CMD:=ftp -V}
PKG_PATH=${PKG_PATH:=/usr/ports/packages/`machine`/all/:${MIRROR}/`uname -r`/packages/`machine`/}

DESTDIR=${DESTDIR:=/}
SYSMERGE=${SYSMERGE:=/usr/sbin/sysmerge}
FORCE_DIR=${FORCE_DIR:=No}

INSTALLED_SETS=${INSTALLED_SETS:=`installed_sets`}

CHECKSUM_TYPES=${CHECKSUM_TYPES:=SHA256.sig SHA256}

set_version
local _error=$?

echo
echo "-= update_openbsd - helper script to update OpenBSD =-"
echo "------------------------------------------------------"
echo
echo "       SYSMERGE: $SYSMERGE"
echo "         MIRROR: $MIRROR"
echo "     RELEASEDIR: $RELEASEDIR"
echo "        DESTDIR: $DESTDIR"
echo "    BOOT_KERNEL: $BOOT_KERNEL"
echo "INSTALL_KERNELS: $INSTALL_KERNELS"
echo " INSTALLED_SETS: $INSTALLED_SETS"
echo
echo "        CUR_VER: $CUR_VER"
echo "        NEW_VER: $NEW_VER"
#echo "       FILE_VER: $FILE_VER"
echo

if [ ${_error} -ne 0 ]; then
	exit ${_error}
fi

if [ X"" != X"${FTP}" ]; then
    get_sets
fi

check_sets || exit

echo "===> Last booted:\n$BOOTED_KERNEL_VERSION"
if [ X"$BOOT_KERNEL_VERSION" != X"$BOOTED_KERNEL_VERSION" \
  -a X"$BOOT_KERNEL_VERSION" != X"$NEW_KERNEL_VERSION" ]; then
    echo "Next boot (unless replaced):\n$BOOT_KERNEL_VERSION"
fi
if [ -n "$NEW_KERNEL_VERSION" ]; then
    echo "===> New $BOOT_KERNEL:\n$NEW_KERNEL_VERSION";
else
    echo "\n!!! WARNING: Will not replace boot kernel $BOOT_KERNEL! !!!\n" >&2
    echo "ctrl+C to cancel, enter to continue anyway" >&2
    local _temp
    read _temp
    NEW_KERNEL_VERSION=$BOOT_KERNEL_VERSION
fi

if [ $CUR_VER != $NEW_VER ]; then
    echo >&2
    echo "!!! You are upgrading between OpenBSD versions.     !!!" >&2
    echo "!!! You should make sure you have a root shell open !!!" >&2
    echo "!!! It is needed in order to run /sbin/oreboot.     !!!" >&2
    echo "!!! sudo MAY NOT WORK after sets are extracted.     !!!" >&2
    echo >&2
    echo "ctrl+C to cancel, enter to continue" >&2
    local _temp
    read _temp
fi

if [ X"$NEW_KERNEL_VERSION" != X"$BOOTED_KERNEL_VERSION" \
     -a ! -e /sbin/oreboot ]; then
    cp /sbin/reboot /sbin/oreboot
    if [ $? -ne 0 ]; then
        echo "Error copying old reboot command!" >&2
        exit 1
    fi
    echo "/sbin/reboot copied to /sbin/oreboot"
fi

install_kernels
install_sets

if [ X"$NEW_KERNEL_VERSION" == X"$BOOTED_KERNEL_VERSION" ]; then
    install_sendmail_smtp_auth

    if [ -e /sbin/oreboot ]; then
        echo Removing /sbin/oreboot
        $rm -f /sbin/oreboot
    fi
    update_etc

    echo '==> UPDATING PACKAGES'
    pkg_add -ui -F update -F updatedepends

else
    echo Instructions for updating to the new version available from 
    if [ X"snapshots" == X"$FORCE_DIR" ]; then
        echo "  http://www.openbsd.org/faq/current.html"
    else
        echo "  http://www.openbsd.org/faq/upgrade${FILE_VER}.html"
    fi
fi

echo Update complete press any key to reboot, ctrl+C to cancel
read _temp
if [ -e /sbin/oreboot ]; then
    /sbin/oreboot
else
    /sbin/reboot
fi