1#!/bin/ksh 2# 3# $OpenBSD: syspatch.sh,v 1.19 2016/11/01 14:45:46 ajacoutot Exp $ 4# 5# Copyright (c) 2016 Antoine Jacoutot <ajacoutot@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 19set -e 20 21sp_err() 22{ 23 echo "${@}" 1>&2 && return 1 24} 25 26usage() 27{ 28 sp_err "usage: ${0##*/} [-c | -l | -r]" 29} 30 31needs_root() 32{ 33 [[ $(id -u) -ne 0 ]] && sp_err "${0##*/}: need root privileges" 34} 35 36apply_patch() 37{ 38 local _explodir _file _files _patch=$1 39 [[ -n ${_patch} ]] 40 41 local _explodir=${_TMP}/${_patch} 42 mkdir -p ${_explodir} 43 44 _files="$(tar xvzphf ${_TMP}/${_patch}.tgz -C ${_explodir})" 45 create_rollback ${_patch} "${_files}" 46 47 for _file in ${_files}; do 48 if [[ ${_file} == @(bsd|bsd.mp) ]]; then 49 if ! install_kernel ${_explodir}/${_file}; then 50 rollback_patch 51 sp_err "Failed to apply ${_patch} (/${_file})" 52 fi 53 else 54 if ! install_file ${_explodir}/${_file} /${_file}; then 55 rollback_patch 56 sp_err "Failed to apply ${_patch} (/${_file})" 57 fi 58 fi 59 done 60} 61 62apply_patches() 63{ 64 needs_root 65 # XXX cleanup old rollback patches and sig (installer should as well) 66 local _m _patch _patches="$(ls_missing)" 67 [[ -n ${_patches} ]] || return 0 # nothing to do 68 69 for _patch in ${_patches}; do 70 fetch_and_verify "${_patch}" 71 apply_patch "${_patch}" 72 done 73 74 # non-fatal: the syspatch tarball should have correct permissions 75 for _m in 4.4BSD BSD.x11; do 76 mtree -qdef /etc/mtree/${_m}.dist -p / -U >/dev/null || true 77 done 78} 79 80create_rollback() 81{ 82 local _file _patch=$1 _rbfiles 83 [[ -n ${_patch} ]] 84 shift 85 local _files="${@}" 86 [[ -n ${_files} ]] 87 88 [[ -d ${_PDIR}/${_REL} ]] || install -d ${_PDIR}/${_REL} 89 90 for _file in ${_files}; do 91 [[ -f /${_file} ]] || continue 92 _rbfiles="${_rbfiles} ${_file}" 93 done 94 95 if ! (cd / && 96 # GENERIC.MP: substitute bsd.mp->bsd and bsd.sp->bsd 97 if ${_BSDMP} && 98 tar -tzf ${_TMP}/${_patch}.tgz bsd >/dev/null 2>&1; then 99 tar -czf ${_PDIR}/${_REL}/rollback-${_patch}.tgz \ 100 -s '/^bsd.mp$//' -s '/^bsd$/bsd.mp/' \ 101 -s '/^bsd.sp$/bsd/' bsd.sp ${_rbfiles} 102 else 103 tar -czf ${_PDIR}/${_REL}/rollback-${_patch}.tgz \ 104 ${_rbfiles} 105 fi 106 ); then 107 rm ${_PDIR}/${_REL}/rollback-${_patch}.tgz 108 sp_err "Failed to create rollback for ${_patch}" 109 fi 110} 111 112fetch_and_verify() 113{ 114 # XXX privsep ala installer 115 local _patch=$1 116 [[ -n ${_patch} ]] 117 118 local _key="/etc/signify/openbsd-${_RELINT}-syspatch.pub" _p 119 120 # XXX handle bogus PATCH_PATH (ftp(1) interactive mode) 121 ${_FETCH} -o "${_TMP}/SHA256.sig" "${PATCH_PATH}/SHA256.sig" 122 123 # XXX see above 124 ${_FETCH} -mD "Applying" -o "${_TMP}/${_patch}.tgz" \ 125 "${PATCH_PATH}/${_patch}.tgz" 126 (cd ${_TMP} && 127 /usr/bin/signify -qC -p ${_key} -x SHA256.sig ${_patch}.tgz) 128} 129 130install_file() 131{ 132 # XXX handle sym/hardlinks? 133 # XXX handle dir becoming file and vice-versa? 134 local _src=$1 _dst=$2 135 [[ -f ${_src} && -f ${_dst} ]] 136 137 local _fmode _fown _fgrp 138 eval $(stat -f "_fmode=%OMp%OLp _fown=%Su _fgrp=%Sg" ${_src}) 139 140 install -DFS -m ${_fmode} -o ${_fown} -g ${_fgrp} ${_src} ${_dst} 141} 142 143install_kernel() 144{ 145 local _bsd=/bsd _kern=$1 146 [[ -n ${_kern} ]] 147 148 # we only save the original release kernel once 149 [[ -f /bsd.rollback${_RELINT} ]] || 150 install -FSp /bsd /bsd.rollback${_RELINT} 151 152 if ${_BSDMP}; then 153 [[ ${_kern##*/} == bsd ]] && _bsd=/bsd.sp 154 fi 155 156 if [[ -n ${_bsd} ]]; then 157 install -FS ${_kern} ${_bsd} 158 fi 159} 160 161ls_avail() 162{ 163 ${_FETCH} -o - "${PATCH_PATH}/index.txt" | 164 sed 's/^.* //;s/^M//;s/.tgz$//' | 165 grep "^syspatch-${_RELINT}-.*$" | sort -V 166} 167 168ls_installed() 169{ 170 local _p 171 # no _REL dir = no installed patch 172 cd ${_PDIR}/${_REL} 2>/dev/null && set -- * || return 0 173 for _p; do 174 [[ ${_p} = rollback-syspatch-${_RELINT}-*.tgz ]] && 175 _p=${_p#rollback-} && echo ${_p%.tgz} 176 done | sort -V 177} 178 179ls_missing() 180{ 181 local _a _installed 182 _installed="$(ls_installed)" 183 184 for _a in $(ls_avail); do 185 if [[ -n ${_installed} ]]; then 186 echo ${_a} | grep -qw -- "${_installed}" || echo ${_a} 187 else 188 echo ${_a} 189 fi 190 done 191} 192 193rollback_patch() 194{ 195 needs_root 196 local _explodir _file _files _patch 197 198 _patch="$(ls_installed | sort -V | tail -1)" 199 [[ -n ${_patch} ]] 200 201 _explodir=${_TMP}/rollback-${_patch} 202 mkdir -p ${_explodir} 203 204 _files="$(tar xvzphf ${_PDIR}/${_REL}/rollback-${_patch}.tgz -C \ 205 ${_explodir})" 206 207 for _file in ${_files}; do 208 if [[ ${_file} == @(bsd|bsd.mp) ]]; then 209 install_kernel ${_explodir}/${_file} || 210 sp_err "Failed to rollback ${_patch} (/${_file})" 211 else 212 install_file ${_explodir}/${_file} /${_file} || 213 sp_err "Failed to rollback ${_patch} (/${_file})" 214 fi 215 done 216 217 rm ${_PDIR}/${_REL}/rollback-${_patch}.tgz \ 218 ${_PDIR}/${_REL}/${_patch#syspatch-${_RELINT}-}.patch.sig 219} 220 221# only run on release (not -current nor -stable) 222set -A _KERNV -- $(sysctl -n kern.version | 223 sed 's/^OpenBSD \([0-9]\.[0-9]\)\([^ ]*\).*/\1 \2/;q') 224[[ -z ${_KERNV[1]} ]] 225 226# check args 227[[ $@ == @(|-[[:alpha:]]) ]] || usage 228 229# XXX to be discussed 230[[ -n ${PATCH_PATH} ]] 231[[ -d ${PATCH_PATH} ]] && PATCH_PATH="file://$(readlink -f ${PATCH_PATH})" 232 233# XXX hw.ncpufound ? 234[[ $(sysctl -n hw.ncpu) -gt 1 ]] && _BSDMP=true || _BSDMP=false 235_FETCH="/usr/bin/ftp -MV -k ${FTP_KEEPALIVE-0}" 236_PDIR="/var/syspatch" 237_REL=${_KERNV[0]} 238_RELINT=${_REL%\.*}${_REL#*\.} 239_TMP=$(mktemp -d -p /tmp syspatch.XXXXXXXXXX) 240readonly _BSDMP _FETCH _PDIR _REL _RELINT _TMP 241 242trap "rm -rf ${_TMP}; exit 1" 2 3 9 13 15 ERR 243 244while getopts clr arg; do 245 case ${arg} in 246 c) ls_missing;; 247 l) ls_installed;; 248 r) rollback_patch;; 249 *) usage;; 250 esac 251done 252shift $(( OPTIND -1 )) 253[[ $# -ne 0 ]] && usage 254 255[[ ${OPTIND} != 1 ]] || apply_patches 256 257rm -rf ${_TMP} 258