FreeBSD Bugzilla – Attachment 146299 Details for
Bug 193003
[PATCH] sysutils/bsdadminscripts: fix some scripts
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
bsdadminscripts.diff
bsdadminscripts.diff (text/plain), 94.46 KB, created by
Carlos J Puga Medina
on 2014-08-26 11:23:20 UTC
(
hide
)
Description:
bsdadminscripts.diff
Filename:
MIME Type:
Creator:
Carlos J Puga Medina
Created:
2014-08-26 11:23:20 UTC
Size:
94.46 KB
patch
obsolete
>diff -urN /usr/ports/sysutils/bsdadminscripts.old/Makefile /usr/ports/sysutils/bsdadminscripts/Makefile >--- /usr/ports/sysutils/bsdadminscripts.old/Makefile 2014-08-25 16:12:18.000000000 +0200 >+++ /usr/ports/sysutils/bsdadminscripts/Makefile 2014-08-26 12:16:02.000000000 +0200 >@@ -1,9 +1,9 @@ > # Created by: Dominic Fandrey <lon_kamikaze@gmx.de> >-# $FreeBSD: head/sysutils/bsdadminscripts/Makefile 366079 2014-08-25 14:12:18Z marino $ >+# $FreeBSD$ > > PORTNAME= bsdadminscripts > PORTVERSION= 6.1.1 >-PORTREVISION= 6 >+PORTREVISION= 7 > CATEGORIES= sysutils ports-mgmt > MASTER_SITES= SF/${PORTNAME}/${PORTNAME} > >@@ -13,10 +13,17 @@ > LICENSE= BSD2CLAUSE > > NO_BUILD= yes >+ >+TMP= /tmp >+VAR= /var >+ > PORTDOCS= ABOUT CHANGES INSTALL NOTES THANKS > > OPTIONS_DEFINE= DOCS > >+SUB_FILES= pkg_libchk pkg_upgrade uma >+SUB_LIST= TMP=${TMP} PREFIX=${PREFIX} VAR=${VAR} PORTS=${PORTSDIR} >+ > .include <bsd.port.options.mk> > > .if ! ${PORT_OPTIONS:MDOCS} >@@ -31,12 +38,14 @@ > -datadir=${STAGEDIR}${DATADIR} \ > ${EVALDOCS} > .for n in pkg_libchk pkg_upgrade uma >+ ${MV} ${WRKDIR}/${n} ${WRKSRC}/src > ${INSTALL_SCRIPT} ${WRKSRC}/src/${n} ${STAGEDIR}${PREFIX}/sbin > .endfor > ${INSTALL_DATA} ${WRKSRC}/src/buildflags.mk ${STAGEDIR}${DATADIR} > ${INSTALL_DATA} ${WRKSRC}/src/buildflags.conf.sample \ > ${STAGEDIR}${PREFIX}/etc > ${INSTALL_DATA} ${WRKSRC}/src/uma.conf.sample ${STAGEDIR}${PREFIX}/etc >+ > .for f in bsdadminscripts buildflags.awk buildflags.conf buildflags.mk \ > distviper pkg_libchk pkg_upgrade pkg_validate portconfig rcstart uma > ${INSTALL_MAN} ${WRKSRC}/src/${f}.1 ${STAGEDIR}${MAN1PREFIX}/man/man1 >@@ -46,6 +55,7 @@ > ${MKDIR} ${STAGEDIR}${ETCDIR} > ${MV} ${STAGEDIR}${PREFIX}/etc/*.sample ${STAGEDIR}${ETCDIR} > ${RM} -rf ${STAGEDIR}${PREFIX}/etc/*.sample >+ > .if ${PORT_OPTIONS:MDOCS} > ${MKDIR} ${STAGEDIR}${DOCSDIR} > cd ${WRKSRC} && ${INSTALL_DATA} ${PORTDOCS} ${STAGEDIR}${DOCSDIR} >diff -urN /usr/ports/sysutils/bsdadminscripts.old/files/patch-pkg_libchk /usr/ports/sysutils/bsdadminscripts/files/patch-pkg_libchk >--- /usr/ports/sysutils/bsdadminscripts.old/files/patch-pkg_libchk 2014-08-25 10:58:15.000000000 +0200 >+++ /usr/ports/sysutils/bsdadminscripts/files/patch-pkg_libchk 1970-01-01 01:00:00.000000000 +0100 >@@ -1,75 +0,0 @@ >---- src/pkg_libchk.orig 2009-04-19 17:57:16.000000000 +0200 >-+++ src/pkg_libchk 2013-06-26 22:01:57.000000000 +0200 >-@@ -23,6 +23,8 @@ >- >- readonly name=pkg_libchk >- readonly version=1.6.1 >-+readonly osname=`uname -s` >-+readonly pkgng=`make -f /usr/share/mk/bsd.port.mk -V WITH_PKGNG` >- >- # Use a line break as delimiter. >- IFS=' >-@@ -206,7 +208,7 @@ dependencyMissing() { >- # We cannot handle non-native binaries, >- # so assume everything is in order. >- if ! readelf -e "$1" 2>&1 | \ >-- grep -E "^[[:space:]]*OS/ABI:[[:space:]]*UNIX - $OSTYPE\$" \ >-+ grep -E "^[[:space:]]*OS/ABI:[[:space:]]*UNIX - $osname\$" \ >- > /dev/null >- then >- return 2 >-@@ -405,10 +407,17 @@ statusSet 'Preparing ...' >- >- # Get the packages to work on. >- test -z "$packages" && packages="-a" >--packages="$(pkg_info -E $packages)" >--test -z "$recursive" -a -z "$Recursive" || packages="$packages >--$(pkg_info -q $recursive $Recursive "$packages" 2> /dev/null | \ >--sed -E 's|^@pkgdep[[:space:]]*||1')" >-+if [ -n "$pkgng" ]; then >-+ packages="$(pkg info -q $packages)" >-+ test -z "$recursive" -a -z "$Recursive" || packages="$packages >-+ $(pkg info -q $recursive $Recursive "$packages" 2> /dev/null | \ >-+ sed -E 's|^@pkgdep[[:space:]]*||1')" >-+else >-+ packages="$(pkg_info -E $packages)" >-+ test -z "$recursive" -a -z "$Recursive" || packages="$packages >-+ $(pkg_info -q $recursive $Recursive "$packages" 2> /dev/null | \ >-+ sed -E 's|^@pkgdep[[:space:]]*||1')" >-+fi >- >- # Create the regexp to match ldd output >- match_expr="$compat=> not found|dependency .+ not found" >-@@ -420,9 +429,15 @@ package_num=0 >- # Check each selected package. >- for package in $packages; { >- package_num="$(($package_num + 1))" >-- test $origin \ >-- && package_name="$(pkg_info -qo "$package")" \ >-- || package_name="$package" >-+ if [ -n "$pkgng" ]; then >-+ test $origin \ >-+ && package_name="$(pkg info -qo "$package")" \ >-+ || package_name="$package" >-+ else >-+ test $origin \ >-+ && package_name="$(pkg_info -qo "$package")" \ >-+ || package_name="$package" >-+ fi >- >- # Print what we're doing. >- statusSet "Starting job $package_num of $package_amount: $package_name" >-@@ -432,7 +447,12 @@ for package in $packages; { >- # Remember freeing the semaphore. >- trap 'semaphoreFree jobs' EXIT >- >-- files="$(pkg_info -qL "$package")" >-+ files="" >-+ if [ -n "$pkgng" ]; then >-+ files="$(pkg info -lq "$package")" >-+ else >-+ files="$(pkg_info -qL "$package")" >-+ fi >- # Get the programs libraries in case it doesn't use the >- # operating system to find its libraries. >- libraries="$(echo "$files" | grep -E '\.so[\.0-9]*$')" >diff -urN /usr/ports/sysutils/bsdadminscripts.old/files/pkg_libchk.in /usr/ports/sysutils/bsdadminscripts/files/pkg_libchk.in >--- /usr/ports/sysutils/bsdadminscripts.old/files/pkg_libchk.in 1970-01-01 01:00:00.000000000 +0100 >+++ /usr/ports/sysutils/bsdadminscripts/files/pkg_libchk.in 2014-08-26 12:13:14.000000000 +0200 >@@ -0,0 +1,484 @@ >+#!/bin/sh -f >+# >+# Copyright (c) 2007-2009 >+# Dominic Fandrey <kamikaze@bsdforen.de> >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# >+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR >+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES >+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. >+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, >+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT >+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF >+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+# >+ >+readonly name=pkg_libchk >+readonly version=1.6.1 >+readonly osname=`uname -s` >+readonly pkgng=`make -f /usr/share/mk/bsd.port.mk -V WITH_PKGNG` >+ >+# Use a line break as delimiter. >+IFS=' >+' >+ >+# Filename prefix for shared data >+sharedprefix="%%TMP%%/$$" >+shared="locks" >+ >+# >+# This function remembers a lock to allow later deletion with the >+# lockUnregisterAll() function. >+# >+# @param $1 >+# The name of the lock. >+lockRegister() { >+ local lock >+ lock="$sharedprefix-$shared" >+ lockf -k "$lock" sh -c " >+ if ! grep -qE '^$1\$' '$lock'; then >+ echo '$1' >> '$lock' >+ fi >+ " >+} >+ >+# >+# Unregisters all locks. >+# >+lockUnregisterAll() { >+ wait >+ for register in $(cat "$sharedprefix-$shared"); { >+ lockf "$sharedprefix-$register" wait >+ } >+ lockf "$sharedprefix-$shared" wait >+} >+ >+# >+# This function creates a semaphore. >+# >+# @param $1 >+# The name of the semaphore. >+# @param $2 >+# The size of the semaphore. >+# >+semaphoreCreate() { >+ local lock >+ lockRegister "semaphore-$1" >+ lock="$sharedprefix-semaphore-$1" >+ lockf -k "$lock" echo "$2" > "$lock" >+ eval "semaphore_$1_size=$2" >+} >+ >+# >+# This function waits until the semaphore is free und registers its use. >+# Everything that uses this also has to call the semaphoreFree() function. >+# >+# @param $1 >+# The name of the semaphore. >+# >+semaphoreUse() { >+ local lock semaphores >+ lock="$sharedprefix-semaphore-$1" >+ while ! lockf -k "$lock" sh -c " >+ state=\$(cat '$lock') >+ if [ \"\$state\" -gt 0 ]; then >+ echo \"\$((\$state - 1))\" > '$lock' >+ exit 0 >+ fi >+ exit 1 >+ "; do >+ sleep 0.1 >+ done >+} >+ >+# >+# This function frees a semaphore. >+# >+# @param $1 >+# The name of the semaphore. >+# >+semaphoreFree() { >+ local lock >+ lock="$sharedprefix-semaphore-$1" >+ lockf -k "$lock" sh -c " >+ state=\"\$((\"\$(cat '$lock')\" + 1))\" >+ echo \"\$state\" > '$lock' >+ " >+} >+ >+# >+# This function sets a new status and prints it. >+# >+# @param $1 >+# The status message. >+# @param $clean >+# If set status handling is disabled. >+# >+statusSet() { >+ # In clean mode status handling is disabled. >+ test -z "$clean" || return 0 >+ local lock >+ lock="$sharedprefix-status" >+ lockf -k "$lock" sh -c " >+ status=\"\$(cat '$lock')\" >+ echo '$1' > '$lock' >+ printf \"\\r%-\${#status}s\\r\" '$1' > /dev/tty >+ " >+} >+ >+# >+# This function prints a message and the current status behind it. >+# >+# @param $1 >+# The message to print. >+# @param $clean >+# If set the status will not be printed. >+# >+statusPrint() { >+ if [ -z "$clean" ]; then >+ local lock >+ lock="$sharedprefix-status" >+ lockf -k "$lock" sh -c " >+ status=\"\$(cat '$lock')\" >+ printf \"%-\${#status}s\\r\" '' > /dev/tty >+ echo '$1' >+ printf '%s\\r' \"\$status\" > /dev/tty >+ " >+ else >+ echo "$1" >+ fi >+} >+ >+# >+# Waits for a semaphore to be completely free and counts down the remaining >+# number of locks. >+# >+# @param $1 >+# The semaphore to watch. >+# @param $2 >+# The status message to print, insert %d in the place where the number >+# of remaining locks belong. >+# >+semaphoreCountDown() { >+ local free size >+ while read -t1 free < "$sharedprefix-semaphore-$1"; do >+ size=$(eval "echo \$semaphore_$1_size") >+ statusSet "$(printf "$2" $(( $size - $free )))" >+ test "$free" -eq "$size" && break >+ sleep 0.1 >+ done >+ wait >+} >+ >+# Clean up upon exit. >+trap ' >+ semaphoreCountDown jobs "Terminated by signal, waiting for %d jobs to die." >+ echo > /dev/tty >+ lockUnregisterAll >+ exit 255 >+' int term >+ >+# >+# This function checks whether a given binary or library directly depends >+# on a missing library. >+# It goes a long way to prevent all kinds of false positives. >+# It always returns 2 (false) for Linux and other non-native libraries >+# and binaries. >+# It also checks whether the missing dependency is really a direct dependency >+# (indirect dependencies have to be fixed somewhere else). >+# >+# @param $1 >+# The library or binary to check. >+# @return >+# Returns 0 (true) if a library is missing. >+# Returns 1 if everything is all right. >+# Returns 2 if the check cannot be performed (not a native library). >+# >+dependencyMissing() { >+ local missing file direct libfound >+ >+ # We cannot handle non-native binaries, >+ # so assume everything is in order. >+ if ! readelf -e "$1" 2>&1 | \ >+ grep -E "^[[:space:]]*OS/ABI:[[:space:]]*UNIX - $osname\$" \ >+ > /dev/null >+ then >+ return 2 >+ # Nothing is missing. >+ elif ! missing="$(ldd "$1" 2>&1 | grep -E "$match_expr")"; then >+ return 1 >+ fi >+ >+ # The return status. The value 1 assumes that this is a false positive. >+ status=1 >+ >+ # Only report misses for direct dependencies. >+ direct="$( >+ readelf -d "$1" 2> /dev/null | \ >+ grep 'Shared library:' | \ >+ sed -E -e 's|^[^[]*\[||1' -e 's|\]$||1' >+ )" >+ >+ # Compare every missing depency with the list of direct dependencies >+ # and report that the dependency is missing if the missing file is >+ # a direct dependency. >+ for file in $missing; { >+ # Strip the missing file of additional information. >+ file="$(echo "$file" | sed -E \ >+ -e 's| => .*$||1' \ >+ -e 's|^[[:space:]]*||1' \ >+ -e 's|^.*dependency ||1' \ >+ -e 's| not found$||1' >+ )" >+ >+ # If in mean mode we do not check for false positives. >+ if [ -n "$mean" ]; then >+ test -n "$raw" && return 0 >+ statusPrint "$package_name: $1 misses $file" >+ continue >+ fi >+ >+ # Handle the case where a library is not found, but exists >+ # somewhere in the package. This is for packages that do not >+ # rely on the OS to find libraries. >+ libfound= >+ for library in $(echo "$libraries" | grep -E "/$file\$"); { >+ # The library exists after all. >+ test -e "$library" && libfound=1 && break >+ } >+ if test "$libfound"; then >+ test -n "$verbose" && statusPrint "$package_name: \ >+located: $1 misses $file found at $library." >+ continue >+ fi >+ >+ # Compare the file with the list of direct dependencies. >+ # If it's not in than it's only an indirect dependency and >+ # cannot be fixed by rebuilding this port. >+ if echo "$direct" | grep -E "^$file\$" > /dev/null; then >+ test -n "$raw" && return 0 >+ statusPrint "$package_name: $1 misses $file" >+ status=0 >+ elif [ -n "$verbose" ]; then >+ statusPrint "$package_name: inderect: $1 \ >+misses $file is an inderect dependency." >+ fi >+ } >+ >+ return $status >+} >+ >+# >+# Checks the parameters for options. >+# >+# @param $packages >+# The parameters to pkg_info -E that will result in the >+# names of the packages to work on. >+# @param $recursive >+# Contains the appropriate parameter to get the >+# dependencies of the given packages from pkg_info. >+# @param $Recursive >+# Contains the appropriate parameter to get the >+# packages depending on the given packages from pkg_info. >+# @param $raw >+# Is set to trigger raw printing. >+# @param $clean >+# Is set to trigger printing without status messages. >+# @param $verbose >+# Is set to be verbose about false positives. >+# @param $mean >+# Is set to switch into mean mode. That means no >+# checking of false positives. >+# @param $compat >+# Delete to avoid detecting compat libraries as misses. >+# @param $origin >+# Is set to turn the print origin mode on. >+# @semaphore jobs >+# Is set to limit the amount of parallel jobs. >+# >+readParams() { >+ local option >+ >+ for option { >+ case "$option" in >+ "-a" | "--all") >+ packages="-a" >+ ;; >+ "-c" | "--clean") >+ clean=1 >+ ;; >+ "-h" | "--help") >+ printHelp >+ ;; >+ -j* | --jobs*) >+ local jobs >+ jobs="${option#-j}" >+ jobs="${jobs#--jobs}" >+ if [ "$jobs" -ne "$jobs" ] 2> /dev/null; then >+ echo "The -j option must be followed" \ >+ "by a number." >+ exit 3 >+ elif [ "$jobs" -lt 1 ]; then >+ echo "The -j option must specify at" \ >+ "least 1 job." >+ exit 3 >+ else >+ semaphoreCreate jobs "$jobs" >+ fi >+ ;; >+ "-m" | "--mean") >+ mean=1 >+ ;; >+ "-n" | "--no-compat") >+ compat= >+ ;; >+ "-o" | "--origin") >+ origin=1 >+ ;; >+ "-q" | "--raw") >+ raw=1 >+ if [ -n "$verbose" ]; then >+ echo "The parameters -v and -q may" \ >+ "not be used at the same time." >+ exit 2 >+ fi >+ ;; >+ "-r" | "--recursive") >+ recursive="-r" >+ ;; >+ "-R" | "--upward-recursive") >+ Recursive="-R" >+ ;; >+ "-v" | "--verbose") >+ verbose=1 >+ if [ -n "$raw" ]; then >+ echo "The parameters -q and -v may" \ >+ "not be used at the same time." >+ exit 2 >+ fi >+ ;; >+ -? | --*) >+ echo "Unknown parameter \"$option\"." >+ exit 1 >+ ;; >+ -*) >+ readParams "${option%${option#-?}}" >+ readParams "-${option#-?}" >+ ;; >+ *) >+ packages="$packages${packages:+$IFS}$option" >+ ;; >+ esac >+ } >+} >+ >+# >+# Display a short help message. >+# >+printHelp() { >+ echo "$name v$version >+usage: $name [-a] [-c] [-h] [-jN] [-m] [-n] [-o] [-q] [-r] [-R] [-v] [packages]" >+ exit 0 >+} >+ >+# Create the expression to match to find files linking against compat libraries. >+# This can be emptied by readParams to deactivate that feature. >+prefix="$(make -f /usr/share/mk/bsd.port.mk -VPREFIX 2> /dev/null || \ >+ echo '%%PREFIX%%')" >+compat="=> $prefix/lib/compat|" >+ >+# Create the semaphore with CPU cores * 2 jobs. >+semaphoreCreate jobs "$(($(sysctl -n hw.ncpu 2> /dev/null || echo 1) * 2))" >+# Register the status lock. >+lockRegister status >+ >+# Read the parameters. >+readParams "$@" >+ >+statusSet 'Preparing ...' >+ >+# Get the packages to work on. >+test -z "$packages" && packages="-a" >+if [ -n "$pkgng" ]; then >+ packages="$(pkg info -q $packages)" >+ test -z "$recursive" -a -z "$Recursive" || packages="$packages >+ $(pkg info -q $recursive $Recursive "$packages" 2> /dev/null | \ >+ sed -E 's|^@pkgdep[[:space:]]*||1')" >+else >+ packages="$(pkg_info -E $packages)" >+ test -z "$recursive" -a -z "$Recursive" || packages="$packages >+ $(pkg_info -q $recursive $Recursive "$packages" 2> /dev/null | \ >+ sed -E 's|^@pkgdep[[:space:]]*||1')" >+fi >+ >+# Create the regexp to match ldd output >+match_expr="$compat=> not found|dependency .+ not found" >+ >+# The packages to check. >+package_amount="$(echo "$packages" | wc -l | sed 's|[[:space:]]||g')" >+package_num=0 >+ >+# Check each selected package. >+for package in $packages; { >+ package_num="$(($package_num + 1))" >+ if [ -n "$pkgng" ]; then >+ test $origin \ >+ && package_name="$(pkg info -qo "$package")" \ >+ || package_name="$package" >+ else >+ test $origin \ >+ && package_name="$(pkg_info -qo "$package")" \ >+ || package_name="$package" >+ fi >+ >+ # Print what we're doing. >+ statusSet "Starting job $package_num of $package_amount: $package_name" >+ >+ semaphoreUse jobs >+ ( >+ # Remember freeing the semaphore. >+ trap 'semaphoreFree jobs' EXIT >+ >+ files="" >+ if [ -n "$pkgng" ]; then >+ files="$(pkg info -lq "$package")" >+ else >+ files="$(pkg_info -qL "$package")" >+ fi >+ # Get the programs libraries in case it doesn't use the >+ # operating system to find its libraries. >+ libraries="$(echo "$files" | grep -E '\.so[\.0-9]*$')" >+ >+ outdated=0 >+ broken= >+ >+ # Check each file of each package. >+ for file in $files; { >+ if [ ! -L "$file" -a \( \ >+ -x "$file" -o \ >+ -n "$(echo "$file" | grep -E '\.so[\.0-9]*$')" \ >+ \) ]; then >+ if dependencyMissing "$file"; then >+ if [ -n "$raw" ]; then >+ statusPrint "$package_name" >+ break 1 >+ fi >+ fi >+ fi >+ } >+ ) & >+} >+ >+semaphoreCountDown jobs "Waiting for %d remaining jobs to finish." >+statusSet >+lockUnregisterAll >+ >+exit 0 >diff -urN /usr/ports/sysutils/bsdadminscripts.old/files/pkg_upgrade.in /usr/ports/sysutils/bsdadminscripts/files/pkg_upgrade.in >--- /usr/ports/sysutils/bsdadminscripts.old/files/pkg_upgrade.in 1970-01-01 01:00:00.000000000 +0100 >+++ /usr/ports/sysutils/bsdadminscripts/files/pkg_upgrade.in 2014-08-26 11:18:27.000000000 +0200 >@@ -0,0 +1,2239 @@ >+#!/bin/sh -f >+# >+# Copyright (c) 2009 >+# Dominic Fandrey <kamikaze@bsdforen.de> >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# >+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR >+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES >+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. >+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, >+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT >+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF >+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+# >+ >+readonly version=1.1 >+readonly name=pkg_upgrade >+ >+# Error table. >+readonly ERR_LOCK=1 >+readonly ERR_ARG=2 >+readonly ERR_INDEX=3 >+readonly ERR_FETCH=4 >+readonly ERR_SORT=5 >+readonly ERR_BACKUP_MISS=6 >+readonly ERR_BACKUP_UNKNOWN=7 >+readonly ERR_INSTALL=8 >+readonly ERR_USER=9 >+readonly ERR_TERM=10 >+readonly ERR_PACKAGE_FORMAT=11 >+readonly ERR_CONFLICT=12 >+ >+# Constant assignments. >+readonly logfile="%%VAR%%/log/$name.log" >+readonly pid=$$ >+ >+# Get some environment variables from uma. This includes PACKAGESITE, >+# TMPDIR and PKG_INDEX. >+eval "$(uma env $pid)" >+ >+# The remote package repository, derived from PACKAGESITE. >+# If this matches the PACKAGES environment variable all downloading operations >+# will be omitted. >+readonly packagerepos="${PACKAGESITE%/*?}" >+ >+# Environment variables. >+: ${PACKAGES="$(make -V PACKAGES -f /usr/share/mk/bsd.port.mk 2> /dev/null)"} >+PACKAGES="${PACKAGES:-%%PORTS%%/packages}" >+: ${PKG_DBDIR=%%VAR%%/db/pkg} >+: ${TMPDIR=%%TMP%%} >+: ${PKG_TMPDIR=$TMPDIR} >+ >+# This is where backup packages will be stored. >+readonly packagebackup="$PACKAGES/$name-backup" >+# This is where the download manager will listen for messages. >+readonly queueMessages="$TMPDIR/pkg_upgrade.messages.queue" >+ >+# Export environment variables to ensure that every tool uses the same ones. >+export ARCH PACKAGEROOT PACKAGESITE FTP_TIMEOUT PKG_INDEX >+export PACKAGEROOT_MIRRORS PACKAGESITE_MIRRORS >+export PACKAGES PKG_DBDIR TMPDIR PKG_TMPDIR >+ >+# Direct index access. >+readonly IDX_PKG=0 >+readonly IDX_ORIGIN=1 >+readonly IDX_PREFIX=2 >+readonly IDX_COMMENT=3 >+readonly IDX_DESCRIPTION=4 >+readonly IDX_MAINTAINER=5 >+readonly IDX_CATEGORIES=6 >+readonly IDX_DIRECTDEPENDS=7 >+readonly IDX_DEPENDS=8 >+readonly IDX_WWW=9 >+readonly IDX_PERLVERSION=10 >+readonly IDX_PERLMODULES=11 >+ >+# Input field seperator without spaces. >+IFS=' >+' >+ >+# Parameter flags. >+pAll= >+pNoBackup= >+pClean= >+pExitOnConflict= >+pForce= >+pFetchOnly= >+pInteractive= >+pJobs= >+pListDiscarded= >+pNoActions= >+pNoLogging= >+pParanoid= >+pRecursive= >+pReplaceConflicts= >+pMoreRecursive= >+pUpwardRecursive= >+pMoreUpwardRecursive= >+pVerbose= >+ >+# The categories for packages. >+older= >+newer= >+unindexed= >+multiple= >+error= >+ >+# A cache for the pkgDepends function. >+dependsChecked= >+ >+# The names of packages that do not have a verified download. >+pending= >+ >+# >+# The list of packages to upgrade. >+# >+ >+# <origin>;<newPackage> >+upgrade= >+upgradeDepends= >+upgradeDepending= >+ >+# The <newOrgin>;<newPackage> part can also be found in $upgrade. >+# <newOrigin>;<newPackage>|<oldOrigin>;<oldPackage> >+replace= >+ >+# A list of dependency substitutions for new packages. >+# <originalOrigin>;<originalName>|<newDependencyOrigin>;<newDependencyName> >+substituteDepends= >+ >+# The current status line. >+status= >+ >+# The ports directory as used in the index file. >+idxports= >+ >+# >+# Table Of Functions >+# In order of appearance. >+# >+# getIndex() Fetch the latest INDEX >+# getLock() Acquire a lock >+# printStatus() Print status messages on the terminal >+# error() Terminate with an error message >+# warn() Print a warning on stderr >+# verbose() Print a message, but only in verbose mode >+# log() Log activity into a log file >+# getIdxEscape() Escape origins and packages for regular expressions >+# getIdxRows() Filter index rows with an escaped expression >+# getIdxRowsEscaped() Filter index rows with an expression >+# getIdxColumn() Get a certain column from index rows >+# pkgAll() Make a list of outdated packages >+# pkgDepends() Check dependencies >+# pkgDepending() Check upwards dependencies >+# pkgDependencies() Run all dependency checks >+# printProgress() Print numerical progress output >+# pkgSort() Sort packages by dependency >+# printTask() Print the tasks to perform for a package >+# pkgList() List all tasks in 'no actions' mode >+# pkgDownload() Download all required packages >+# pkgUpgrade() Upgrade all scheduled packages >+# substituteDepends() Adjust dependencies of upgraded packages >+# upgradePackage() Upgrade a given package >+# identifyPackage() Identify a package by a user given string >+# printHelp() Print program parameters and terminate >+# readParams() Read the command line parameters >+# readContents() Read the +CONTENTS of a package file >+# downloadManager() Start a background download manager >+# downloadManagerFetch() >+# Try to fetch a package from a mirror >+# downloadManagerMsgRetry() >+# Tell the download manager to retry a download >+# downloadManagerMsgFinished() >+# Tell the download manager a download has been completed >+# downloadManagerMsgRequest() >+# Request a download from the download manager >+# downloadManagerMsgExit() >+# Tell the download manager to terminate >+# validatePackage() Validate a downloaded package >+# >+ >+ >+# >+# Update the local copy of the index and start the download manager. >+# >+# @param idxports >+# This is set to the ports directory used in the index file. This is >+# required for many index operations. If already set the index is >+# assumed to be up to date and nothing is done. >+# @param pVerbose >+# Activate verbose output. >+# >+getIndex() { >+ # The index has already been updated. >+ if [ -n "$idxports" ]; then >+ return 0 >+ fi >+ >+ # Free the lock upon termination. >+ trap "uma unlock $pid" EXIT >+ >+ # First acquire the lock. >+ getLock >+ >+ verbose "Synchronize the local index copy with the package server." >+ >+ # Try to update the index. >+ if ! uma $pVerbose fetch ftpindex $pid; then >+ exit $ERR_INDEX >+ fi >+ >+ # Set the ports directory used in the index. >+ idxports="$(getIdxColumn $IDX_ORIGIN "$(head -n 1 "$PKG_INDEX")")" >+ idxports="${idxports%/*/*}" >+ >+ # Start the download manager. >+ downloadManager >+} >+ >+# >+# Acquires the uma (Update Manager) lock. And spawns a process that locks >+# onto PKG_DBDIR to block the ports from messing with us. >+# >+getLock() { >+ # Acquire the lock. >+ if ! uma lock $pid; then >+ if [ "$USER" != "root" ]; then >+ error $ERR_LOCK "The command $name has to be run as root." >+ else >+ error $ERR_LOCK "The uma (Update MAnager) lock could not be acquired, it appears the package/ports infrastructure is in use." >+ fi >+ fi >+ >+ # Lock onto PKG_DBDIR to avoid ports getting into our way. >+ # The ports tree locks onto PKG_DBDIR during install and deinstall. >+ # Since it does not use uma we use this lock to make sure the ports >+ # tree does not get into our way later. >+ if ! lockf -kst 0 "$PKG_DBDIR" sh -c "lockf -k '$PKG_DBDIR' sh -c 'while kill -0 $pid 2> /dev/null; do sleep 2; done' &"; then >+ error $ERR_LOCK "Locking $PKG_DBDIR failed, the ports tree might be in use." >+ fi >+} >+ >+# >+# Prints a status message to the terminal device /dev/tty. >+# >+# @param 1 >+# The message to print >+# @param status >+# The last printed message, used for clearing the status line before >+# printing a new status. >+# @param pClean >+# If set, do not print status messages. >+# >+printStatus() { >+ test -n "$pClean" && return 0 >+ printf "\r%${#status}s\r%s\r" '' "$1" > /dev/tty >+ status="$1" >+} >+ >+# >+# Exits with the given error and message on stderr. >+# >+# @param 1 >+# The error number to exit with. >+# @param 2 >+# The message to exit with. >+# >+error() { >+ # Clear the status line. >+ printStatus >+ echo "$name: $2" 1>&2 >+ exit "$1" >+} >+ >+# >+# Writes a warning message to stderr. >+# >+# @param 1 >+# The message to write. >+# >+warn() { >+ # Clear the status line. >+ printStatus >+ echo "$name: $1" 1>&2 >+} >+ >+# >+# Outputs verbose messages on stdout. >+# >+# @param @ >+# All the parameters to be output. >+# @param pVerbose >+# If this is not set, do not output anything. >+# >+verbose() { >+ test -z "$pVerbose" && return 0 >+ echo "$@" >+} >+ >+# >+# Logs the given message into a log file. >+# >+# The following format is used. >+# >+# <UTC timestamp> - <date> - (<error>|DONE): <message> >+# >+# UTC timestamp := The output of 'date -u '+%s' >+# date := The output of 'date' >+# >+# @param 1 >+# The error number for the log, if this is 0, the message will be >+# preceded by "DONE:" instead of "ERROR($1):". >+# @param 2 >+# The message to log. >+# @param logfile >+# The name of the file to log into. >+# @param pNoLogging >+# If set, logging is not performed. >+# >+log() { >+ test -n "$pNoLogging" && return 0 >+ >+ if [ $1 -eq 0 ]; then >+ echo "$(date -u '+%s') - $(date) - DONE: $2" >> $logfile >+ else >+ echo "$(date -u '+%s') - $(date) - ERROR($1): $2" >> $logfile >+ fi >+} >+ >+# >+# An escape function for package names fed to the getIdxColumn function. >+# This function reads from the standard input unless a file is named >+# in the parameters. >+# Note that the escaping is done for extended regular expressions, however >+# only characters that can appear in package names are escaped. >+# >+# @param @ >+# More parameters can be added to the sed command. >+# >+getIdxEscape() { >+ sed -E -e 's/([+.])/\\\1/g' "$@" >+} >+ >+# >+# Outputs all rows of the index that match a given pattern in a column. >+# The pattern should not match '|'. >+# >+# @param 1 >+# The column that has to match the pattern. >+# @param 2 >+# The pattern that has to be matched, an extended regular expression. >+# @param 3 >+# Optional, the rows to match against instead of using the index file. >+# >+getIdxRows() { >+ if [ -z "$3" ]; then >+ grep -E "^([^|]*\|){$1}($2)(\|.*)?\$" "$PKG_INDEX" >+ else >+ echo "$3" | grep -E "^([^|]*\|){$1}($2)(\|.*)?\$" >+ fi >+} >+ >+# >+# Outputs all rows of the index that match a given string. >+# The string should not contain '|'. >+# >+# @param 1 >+# The column that has to match the string. >+# @param 2 >+# The string that has to be matched. >+# @param 3 >+# Optional, the rows to match against instead of using the index file. >+# >+getIdxRowsEscaped() { >+ getIdxRows $1 "$(echo "$2" | getIdxEscape)" "$3" >+} >+ >+# >+# Outputs a column of each index row piped into it. >+# >+# @param 1 >+# The column to output. >+# @param 2 >+# The rows to output the columns from. >+# >+getIdxColumn() { >+ echo "$2" | sed -E "s,^([^|]*\|){$1}([^|]*)\|.*,\2,1" >+} >+ >+# >+# Stores all the packages not in sync with the index file in categories. >+# >+# @param older >+# The list of packages older than those in the index. >+# @param newer >+# The list of packages newer than those in the index. >+# @param unindexed >+# The list of packages not in the index. >+# @param multiple >+# The list of packages that have multiple index entries. >+# @param error >+# The list of packages with broken package database entries. >+# @param pForce >+# If set, register all installed packages in the index as outdated. >+# @param pAll >+# If set, add all outdated packages to the list of packages to upgrade. >+# @param pListDiscarded >+# If set, list all the packages that are ignored. >+# @param upgrade >+# The list to add packages to if pAll is set. >+# >+pkgAll() { >+ local package pkgname origin operator row discarded >+ >+ # There's nothing to be done if all of the following conditions are >+ # met: >+ # - Nothing is yet listed for upgrading, so we do not need a list >+ # of outdated packages for dependency checking. >+ # - The updating of all packages is not requested. >+ # - The listing of ignored (i.e. not indexed) packages is not >+ # requested. >+ test -z "$upgrade" -a -z "$pAll" -a -z "$pListDiscarded" && return 0 >+ >+ verbose "Make a list of outdated packages." >+ >+ printStatus "Reading version information of installed packages ..." >+ >+ if [ -n "$pForce" ]; then >+ # In force mode it is assumed that all installed packages to >+ # be found in the index are outdated. >+ for package in $(pkg_version -Io "${PKG_INDEX}"); { >+ origin="${package%% *}" >+ row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" >+ pkgname="$(getIdxColumn $IDX_PKG "$row")" >+ printStatus "Checking <$pkgname>." >+ operator="${package##* }" >+ case "$operator" in >+ '?') >+ unindexed="$unindexed${unindexed:+$IFS}$origin" >+ ;; >+ '!') >+ error="$error${error:+$IFS}$origin" >+ ;; >+ *) >+ older="$older${older:+$IFS}$origin;$pkgname" >+ ;; >+ esac >+ } >+ else >+ # Categorize installed packages and their relations to the >+ # index. >+ for package in $(pkg_version -IoL = ${PKG_INDEX}); { >+ origin="${package%% *}" >+ row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" >+ pkgname="$(getIdxColumn $IDX_PKG "$row")" >+ printStatus "Checking <${pkgname:-$(pkg_info -qO $origin)}>." >+ operator="${package##* }" >+ case "$operator" in >+ '<') >+ older="$older${older:+$IFS}$origin;$pkgname" >+ ;; >+ '>') >+ newer="$newer${newer:+$IFS}$origin;$pkgname" >+ ;; >+ '?') >+ unindexed="$unindexed${unindexed:+$IFS}$origin" >+ ;; >+ '*') >+ multiple="$multiple${multiple:+$IFS}$origin" >+ ;; >+ '!') >+ error="$error${error:+$IFS}$origin" >+ ;; >+ esac >+ } >+ fi >+ >+ printStatus "Assemble checked packages ..." >+ >+ # Remove packages to upgrade from the list of outdated packages. >+ for package in $upgrade; { >+ older="$(echo "$older" | grep -vx "$package")" >+ } >+ >+ # Append outdated packages to the list of packages to update if all >+ # packages are to be updated. >+ if [ -n "$pAll" ]; then >+ downloadManagerMsgRequest "$older" >+ upgrade="$upgrade${older:+${upgrade:+$IFS}}$older" >+ older= >+ fi >+ >+ # Clear the status line. >+ printStatus >+ >+ # Print the discarded packages. >+ if [ -n "$pListDiscarded" ]; then >+ verbose "List discarded packages." >+ >+ discarded="$unindexed$IFS$multipleIFS$error" >+ discarded="$(echo "$discarded" | grep -vFx '' | sort -u)" >+ >+ test -n "$discarded" && echo "$discarded" >+ fi >+} >+ >+# >+# Adds all missing dependencies to the list of packages to upgrade. >+# >+# @param 1 >+# This is used to check the dependencies of newly added depending >+# packages. >+# @param upgrade >+# The primary list of packages to upgrade (read only). >+# @param upgradeDepends >+# The list to add packages to upgrade to. >+# @param older >+# The list of outdated packages. Packages for upgrading are removed from >+# it. >+# @param dependsChecked >+# A list of already checked dependencies, to avoid double checks. >+# @param pRecursive >+# If set, also add outdated dependencies to the upgrade list. >+# @param pMoreRecursive >+# If set, also update the dependencies of depending packages. >+# @param pForce >+# If set together with pRecursive, add all dependencies to the upgrade >+# list. >+# >+pkgDepends() { >+ local pkgname package row rows depends origin escapedPkg upgradeList >+ >+ printStatus "Preparing dependency checks ..." >+ >+ # In thorough mode the depencies of depending packages are updated, too. >+ upgradeList="${1:-$upgrade}" >+ >+ # Luckily packages know their indirect dependencies, too. This way >+ # it is not necessary to check for dependencies recursively. >+ depends= >+ for package in $upgradeList; { >+ row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/${package%;*}")" >+ row="$(getIdxColumn $IDX_DEPENDS "$row")" >+ depends="$depends${depends:+${row:+ }}$row" >+ } >+ >+ # Reformat depends and throw out duplicates. >+ depends="$( >+ echo "$depends" | sed "s/ /\\$IFS/g" | sort -u >+ )" >+ >+ # Do some prefiltering. >+ rows="$(getIdxRowsEscaped $IDX_PKG "$(echo "$depends" | rs -TC\|)")" >+ >+ # Check for missing or outdated dependencies. >+ for pkgname in $depends; { >+ escapedPkg="$(echo "$pkgname" | getIdxEscape)" >+ >+ # Skip packages already checked. >+ if echo "$dependsChecked" | grep -qFx "$pkgname"; then >+ continue >+ fi >+ dependsChecked="$dependsChecked${dependsChecked:+$IFS}$pkgname" >+ >+ printStatus "Check dependency <$pkgname>." >+ >+ # Skip this if this package is already scheduled for updating. >+ if echo "$upgrade${upgradeDepending:+$IFS$upgradeDepending}" | grep -qF ";$pkgname"; then >+ continue >+ fi >+ >+ row="$(getIdxRows $IDX_PKG "$escapedPkg" "$rows")" >+ >+ # If this package could not be identified this is an index >+ # incosistency, that can only be ignored. >+ if [ -z "$row" ]; then >+ warn "Ignore index inconsistency, the dependency <$pkgname> is not in the index." 1>&2 >+ continue >+ fi >+ >+ origin="$(getIdxColumn $IDX_ORIGIN "$row")" >+ origin="${origin#$idxports/}" >+ package="$origin;$(getIdxColumn $IDX_PKG "$row")" >+ >+ # >+ # Deal with dependencies according to set parameters. >+ # >+ if [ -z "$(pkg_info -qO "$origin")" ]; then >+ # The depency is not installed. >+ upgradeDepends="$upgradeDepends${upgradeDepends:+$IFS}$package" >+ # Request a package download. >+ downloadManagerMsgRequest "$package" >+ elif [ -n "$pMoreRecursive" -o -n "$pRecursive" -a -z "$1" ]; then >+ # Check whether the dependency is outdated. >+ if echo "$older" | grep -qFx "$package"; then >+ upgradeDepends="$upgradeDepends${upgradeDepends:+$IFS}$package" >+ older="$(echo "$older" | grep -vFx "$package")" >+ # Request a package download. >+ downloadManagerMsgRequest "$package" >+ fi >+ fi >+ } >+} >+ >+# >+# Checks whether packages depending on the packages to update require updating. >+# >+# @param 1 >+# This is used to check the depending packages of newly added >+# dependencies. >+# @param older >+# The list of outdated packages. If pForce is set, this includes all >+# installed packages listed in the index. >+# @param upgrade >+# The primary list of packages to upgrade (read only). >+# @param upgradeDepending >+# The list of depending packages to upgrade. >+# @param pUpwardRecursive >+# If not set nothing is done. >+# @param pMoreUpwardRecursive >+# Also check the depending packages of depencencies. >+# @param pAll >+# If this is set do nothing. >+# >+pkgDepending() { >+ # Without the upwardRecursive option this is completely >+ # unnecessary. >+ if [ -z "$pUpwardRecursive" ]; then >+ return 0 >+ fi >+ >+ # If all packages are already going to be upgraded, there is no >+ # need for this. >+ if [ -n "$pAll" ]; then >+ return 0 >+ fi >+ >+ # Only update depending packages of dependencies in thorough mode. >+ if [ -n "$1" -a -z "$pMoreUpwardRecursive" ]; then >+ return 0 >+ fi >+ >+ local package pkgname origin row depends escapedPkg upgradeList >+ >+ printStatus "Preparing upwards dependency checks ..." >+ >+ # In thorough mode the depencies of depending packages are updated, too. >+ upgradeList="${1:-$upgrade}" >+ >+ # Do some prefiltering. >+ rows="$(getIdxRowsEscaped $IDX_ORIGIN "$( >+ echo "$older" | rs -TC\| | sed -E "s'([^;|]*);[^|]*'$idxports/\1'g" >+ )")" >+ >+ # For each outdated package, check whether it depends on a package >+ # to upgrade. In force mode outdated packages are all packages, so >+ # the difference does not have to be made here. >+ for package in $older; { >+ # Skip this if this package is already scheduled for updating. >+ if echo "$upgrade${upgradeDepends:+$IFS$upgradeDepends}${upgradeDepending:+$IFS$upgradeDepending}" | grep -qFx "$package"; then >+ continue >+ fi >+ >+ printStatus "Check for upwards dependency <${package#*;}>." >+ >+ origin="${package%;*}" >+ row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" >+ >+ # Ignore unindexed packages. >+ if [ -z "$row" ]; then >+ continue >+ fi >+ >+ depends="$(getIdxColumn $IDX_DEPENDS "$row")" >+ >+ # It has no dependencies, so it cannot depend on anything >+ # in the upgrade list. >+ if [ -z "$depends" ]; then >+ continue >+ fi >+ >+ # Reformat dependencies. >+ depends="$(echo "$depends" | sed -Ee "s/([^ ]+)/;\1/g" -e "s/ /\\$IFS/g")" >+ >+ # Check every dependency for matching the upgrade packages. >+ if echo "$upgradeList" | grep -qF "$depends"; then >+ upgradeDepending="$upgradeDepending${upgradeDepending:+$IFS}$package" >+ older="$(echo "$older" | grep -vFx "$package")" >+ downloadManagerMsgRequest "$package" >+ fi >+ } >+} >+ >+# >+# This function calls pkgDepending and pkgDepends until no new packages >+# show up for updating. All the clever stuff happens in those functions. >+# >+# @param upgrade >+# The list of packages to upgrade. >+# @param upgradeDepends >+# The list of dependencies to add to the list of packages to upgrade. >+# @param upgradeDepending >+# The list of depending packages to add to the list of packages >+# to upgrade. >+# >+pkgDependencies() { >+ test -z "$upgrade" && return 0 >+ >+ verbose "Perform dependency checks." >+ >+ # Run the primary dependency checks. >+ pkgDepending >+ downloadManagerMsgRequest "$upgradeDepending" >+ pkgDepends >+ downloadManagerMsgRequest "$upgradeDepends" >+ >+ # The idea is to keep on checking until nothing new shows up. >+ # Whether that is the case depends on the level of recursiveness. >+ while [ -n "$upgradeDepends$upgradeDepending" ]; do >+ if [ -n "$upgradeDepends" ]; then >+ # Deal with packages depending on the updated packages. >+ pkgDepending "$upgradeDepends" >+ upgrade="$upgradeDepends$IFS$upgrade" >+ upgradeDepends= >+ fi >+ >+ if [ -n "$upgradeDepending" ]; then >+ # Deal with missing or outdated dependencies. >+ pkgDepends "$upgradeDepending" >+ upgrade="$upgrade$IFS$upgradeDepending" >+ upgradeDepending= >+ fi >+ done >+ >+ # Clear the status line. >+ printStatus >+} >+ >+# >+# Prints a progress message to the terminal device /dev/tty. >+# >+# @param 1 >+# Total amount of operations to do. >+# @param 2 >+# The amount of operations performed. >+# @param 3 >+# The name of the package that is currently operated on. >+# @param 4 >+# The text prepending the progress information. >+# @param status >+# The last printed message, used for clearing the status line before >+# printing a new status. >+# @param pClean >+# If set, do not print progress messages. >+# >+printProgress() { >+ test -n "$pClean" && return 0 >+ printf "\r%${#status}s\r$4 %${#1}s of %${#1}s (%3s%%) <$3>.\r" '' "$2" "$1" "$(($2 * 100 / $1))" > /dev/tty >+ status="$4 $1 of $1 (100%) <$3>." >+} >+ >+# >+# Sorts the packages to upgrade by dependency. >+# >+# The trick is to have a list of already sorted packages. Each package added >+# to the list is inserted right behind its last dependency already present >+# there. >+# Packages without any dependencies in the sorted list are prepended. This >+# way it is ensured that they end up before all already sorted packages >+# that depend on them, without additional checking. >+# >+# @param upgrade >+# The list of packages to sort. >+# @param pParanoid >+# If set, make cyclic dependency checks. >+# >+pkgSort() { >+ local rows sorted package row depends dependency pkgname >+ local totalCount count >+ >+ test -z "$upgrade" && return 0 >+ >+ verbose "Sort packages by dependency." >+ >+ printStatus "Prepare sorting of packages ..." >+ >+ # Limit rows to whatever is currently required. >+ rows="$(getIdxRowsEscaped $IDX_ORIGIN "$( >+ echo "$upgrade" | getIdxEscape -e 's/;.*//1' -e "s,^,$idxports/,1" | rs -TC\| >+ )")" >+ >+ # The number of packages >+ totalCount=$(($(echo "$upgrade" | wc -l))) >+ count=0 >+ >+ # Sort each package into the list of sorted packages. >+ sorted= >+ for package in $upgrade; { >+ count=$(($count + 1)) >+ pkgname="${package#*;}" >+ printProgress $totalCount $count "$pkgname" 'Sort' >+ >+ # Get the list of dependencies that should be updated before >+ # the current package. >+ row="$(getIdxRowsEscaped $IDX_PKG "$pkgname" "$rows")" >+ depends="$(getIdxColumn $IDX_DEPENDS "$row" | sed -E "s/ /\\$IFS/g")" >+ >+ # Get the last matching dependency in the list. >+ dependency="$(echo "$sorted" | grep -Fx "$depends" | tail -n 1)" >+ >+ # If there is no match, just prepend to the list. >+ if [ -z "$dependency" ]; then >+ sorted="$pkgname${sorted:+$IFS$sorted}" >+ continue >+ fi >+ >+ # Insert right behind the match. >+ dependency="$(echo "$dependency" | getIdxEscape)" >+ sorted="$(echo "$sorted" | sed -E "s/^$dependency$/$dependency\\$IFS$pkgname/1")" >+ } >+ >+ # Perform optional cyclic dependency check. >+ if [ -n "$pParanoid" ]; then >+ printStatus "Validate sorting order ..." >+ >+ # Validate the sort order. >+ count=0 >+ for pkgname in $sorted; { >+ count=$(($count + 1)) >+ printProgress $totalCount $count "$pkgname" 'Validate' >+ >+ # Get the list of dependencies that should be updated before >+ # the current package. >+ row="$(getIdxRowsEscaped $IDX_PKG "$pkgname" "$rows")" >+ depends="$(getIdxColumn $IDX_DEPENDS "$row" | sed -E "s/ /\\$IFS/g")" >+ >+ # Append the package to the list of dependencies to match. >+ depends="${depends:+$depends$IFS}$pkgname" >+ >+ # Get the last match in the list. >+ dependency="$(echo "$sorted" | grep -Fx "$depends" | tail -n 1)" >+ # The last match has to be the package. >+ if [ "$dependency" != "$pkgname" ]; then >+ error $ERR_SORT "The package <$pkgname> was not sorted properly, a likely cause is a circular dependency." >+ fi >+ } >+ fi >+ >+ printStatus "Assemble sorted packages ..." >+ >+ # Replace package names with <origin>;<package> pairs. >+ for package in $upgrade; { >+ pkgname="$(echo "${package#*;}" | getIdxEscape)" >+ sorted="$(echo "$sorted" | sed -E "s'^$pkgname\$'$package'1")" >+ } >+ >+ upgrade="$sorted" >+ printStatus >+} >+ >+# >+# Prints the update/replace/install task. >+# >+# @param 1 >+# The package to upgrade/install. >+# @param replace >+# The list of packages to replace. >+# >+printTask() { >+ local package newPkgname newOrigin oldPkgname oldOrigin >+ >+ # Get the name and origin of the new package. >+ newPkgname="${1#*;}" >+ newOrigin="${1%;*}" >+ >+ # Look for a package the new one replaces. >+ package="$(echo "$replace" | grep -F "$1|")" >+ >+ # Look for a package this one replaces. >+ # The current package actually replaces another one. >+ if [ -n "$package" ]; then >+ # Get the name and origin of the old package. >+ package="${package#*|}" >+ oldPkgname="${package#*;}" >+ oldOrigin="${package%;*}" >+ >+ echo "Replace <$oldPkgname> ($oldOrigin) with <$newPkgname> ($newOrigin)" >+ return 0 >+ fi >+ >+ # Check whether there's an old version of this package around. >+ package="$(pkg_info -qO "$newOrigin")" >+ >+ # An older package with this origin is installed. >+ if [ -n "$package" ]; then >+ echo "Update <$package> to <$newPkgname> ($newOrigin)" >+ return 0 >+ fi >+ >+ # Aparently this package will be newly installed. >+ echo "Install <$newPkgname> ($newOrigin)" >+} >+ >+# >+# List the packages that are going to be upgraded, installed and replaced. >+# If the 'no actions' mode is active. >+# >+# @param upgrade >+# The list of packages to upgrade. >+# @param pNoActions >+# Print the list of tasks. >+# >+pkgList() { >+ # Only list packages in "no actions" mode. >+ test -z "$pNoActions" && return 0 >+ >+ test -z "$upgrade" && return 0 >+ >+ local package >+ >+ verbose "The following packages will be updated:" >+ >+ for package in $upgrade; { >+ printTask "$package" >+ } >+} >+ >+# >+# Wait for downloaded packages and validate them. >+# >+# @param upgrade >+# The list of packages to download. >+# @param pending >+# The list of pending downloads. >+# @param packagerepos >+# The location of the remote package repository (derived from >+# PACKAGESITE). If this is identical with the local repository, >+# the download manager was not started. >+# @param pNoActions >+# Do not download anything. >+# >+pkgDownload() { >+ test -n "$pNoActions" && return 0 >+ >+ test -z "$upgrade" && return 0 >+ >+ local package total count line >+ >+ verbose "Validate downloaded packages." >+ >+ printStatus "Waiting for downloads ..." >+ >+ # Create a list of the package names to validate. >+ # Entries are removed from this list by validatePackage(). >+ pending="$(echo "$upgrade" | sed 's/.*;//1')" >+ >+ # The total number of packages to validate. >+ total="$(($(echo "$upgrade" | wc -l)))" >+ >+ # Check whether the download manager is available. >+ if [ "$PACKAGES" = "$packagerepos" ]; then >+ # >+ # The local repository is identical with the remote repository >+ # so the assumption is all packages should already be there. >+ # >+ >+ # Validate all packages. >+ for package in $pending; { >+ count=$(($count + 1)) >+ printProgress $total $count "$package" "Validate" >+ validatePackage "$package" >+ } >+ else >+ # >+ # The download manager is available, so hang on to its message >+ # queue and proceed with validating as packages are finished. >+ # >+ count=0 >+ >+ while [ -n "$pending" ]; do >+ read line >+ case "$line" in >+ finished:*) >+ count=$(($count + 1)) >+ package="${line##*;}" >+ printProgress $total $count "$package" "Validate" >+ validatePackage "$package" >+ ;; >+ esac >+ done < "$queueMessages" >+ >+ # Stop the download manager. >+ downloadManagerMsgExit >+ fi >+ >+ # Clear the status line. >+ printStatus >+} >+ >+# >+# Upgrade each package. >+# >+# @param upgrade >+# The list of packages to upgrade. >+# @param conflictReplace >+# This list is reset for conflict handling. >+# @param pNoActions >+# Do not update anything. >+# @param pFetchOnly >+# Do not update anything. >+# >+pkgUpgrade() { >+ test -n "$pNoActions" -o -n "$pFetchOnly" && return 0 >+ >+ test -z "$upgrade" && return 0 >+ >+ local package >+ >+ verbose "Install $(($(echo "$upgrade" | wc -l))) package(s)." >+ >+ for package in $upgrade; { >+ upgradePackage "$package" >+ } >+} >+ >+# >+# To handle conflicts this function removes dependencies from a given package >+# and appends one or more new ones to take their place. Also the +REQUIRED_BY >+# files of the appended dependencies are updated. >+# >+# @param 1 >+# The name of the package to which to apply the substitutions. >+# @param substituteDepends >+# The list of dependency substitutions that should take place. >+# >+substituteDepends() { >+ # End here if there's nothing to substitute. >+ test -z "$substituteDepends" && return 0 >+ >+ local line originalOrigin originalPkgname newOrigin newPkgname >+ local contents append remove requiredBy >+ >+ printStatus "Adjust the dependencies of <$1> ..." >+ >+ # Get the contents file. >+ contents="$(cat "$PKG_DBDIR/$1/+CONTENTS")" >+ >+ # Because there can be several substitutions for a single package >+ # the new ones will be added to the end of the +CONTENTS file and all >+ # the matches will be removed later. >+ append= >+ remove= >+ for line in $substituteDepends; { >+ # Get original origin and package name from the line. >+ originalOrigin="${line%%;*}" >+ originalPkgname="${line%|*}" >+ originalPkgname="${originalPkgname#*;}" >+ >+ # Continue with the next line if this one does not match. >+ if ! echo "$contents" | grep -qFx "@pkgdep $originalPkgname"; then >+ continue >+ fi >+ >+ # Get new origin and package name from the line. >+ newOrigin="${line#*|}" >+ newPkgname="${newOrigin#*;}" >+ newOrigin="${newOrigin%;*}" >+ >+ warn "Add dependency <$newPkgname> ($newOrigin)." >+ >+ # Remember what to append and what to remove. >+ remove="${remove:+$remove$IFS}@pkgdep $originalPkgname$IFS@comment DEPORIGIN:$originalOrigin" >+ # Just for the very unlikely case that two dependencies get >+ # replaced for conflicting with the same package, check that >+ # a dependency is not added twice. >+ if ! echo "$append" | grep -qFx "@pkgdep $newPkgname"; then >+ append="$append$IFS@pkgdep $newPkgname$IFS@comment DEPORIGIN:$newOrigin" >+ fi >+ >+ # Make an entry for the package in the +REQUIRED_BY file of >+ # of the dependency to append. >+ requiredBy="$(cat "$PKG_DBDIR/$newPkgname/+REQUIRED_BY" 2> /dev/null)" >+ requiredBy="${requiredBy:+$requiredBy$IFS}$1" >+ echo "$requiredBy" | sort -u > "$PKG_DBDIR/$newPkgname/+REQUIRED_BY" >+ } >+ >+ # Remove the original dependency entries. >+ contents="$(echo "$contents" | grep -vFx "$remove")" >+ # Write the new file. Note that $append always starts with a newline. >+ echo "$contents$append" > "$PKG_DBDIR/$1/+CONTENTS" >+} >+ >+# >+# Install the given package. This is where the magic happens. >+# >+# @param replace >+# The list of packages to replace (read only). >+# @param substituteDepends >+# A list of dependency substitutions that should take place for each >+# newly installed package to resolve conflicting packages. >+# @param packagebackup >+# The location for backup packages. This is derived from PACKAGES. >+# @param pNoBackup >+# If set, delete backups after successful completion. >+# >+upgradePackage() { >+ local task targetPackage targetPkgname targetOrigin package replace >+ local escapedPkg removePackages origin file conflict conflicting >+ local replacePkgdep requiredBy count >+ local signal >+ >+ # Get a string with the current upgrade task. >+ task="$(printTask "$1")" >+ echo "===> $task" >+ >+ targetPackage="$1" >+ targetPkgname="${1#*;}" >+ targetOrigin="${1%;*}" >+ >+ printStatus "Prepare installation of <$targetPkgname> ..." >+ >+ # Get the packages to replace with this one. Several packages can be >+ # replaced with a single one. >+ escapedPkg="$(echo "$targetPackage" | getIdxEscape)" >+ replace="$(echo "$replace" | grep -Ex "$escapedPkg\|.*" | sed -E "s'^$escapedPkg\|''1")" >+ >+ # Append the current package to the list of packages to replace. >+ replace="${replace:+$replace$IFS}$targetPackage" >+ >+ # Create the list of outdated packages that have to be backed up >+ # and for which pkgdb adjustments have to be made after successful >+ # installation of the new package. >+ # Also create the necessary sed expressions to update the >+ # package database. >+ removePackages= >+ replacePkgdep= >+ for package in $replace; { >+ origin="${package%;*}" >+ package="$(pkg_info -qO "$origin")" >+ test -z "$package" && continue >+ removePackages="$removePackages${removePackages:+$IFS}$package" >+ package="$(echo "$package" | getIdxEscape)" >+ replacePkgdep="$replacePkgdep -e 's|^@pkgdep $package\$|@pkgdep $targetPkgname|1'" >+ if [ "$origin" != "$targetOrigin" ]; then >+ replacePkgdep="$replacePkgdep -e 's|^@comment DEPORIGIN: $origin\$|@comment DEPORIGIN:$targetOrigin|1'" >+ fi >+ >+ } >+ >+ # Get a list of conflicting packages. The conflicts list is >+ # provided by readContents(). >+ readContents "$PACKAGES/All/$targetPkgname.tbz" >+ conflicting= >+ for conflict in $conflicts; { >+ # Match the conflict pattern against installed packages. >+ for conflict in $(pkg_info -E "$conflict"); { >+ escapedPkg="$(echo "$conflict" | getIdxEscape)" >+ # Only add to the conflicting list if the conflicting >+ # package is not in the list of packages to replace. >+ if ! echo "$removePackages" | grep -qEx "$escapedPkg"; then >+ conflicting="${conflicting:+$conflicting$IFS}$conflict" >+ fi >+ } >+ } >+ # Remove duplicated entries. >+ conflicting="$(echo "$conflicting" | sort -u)" >+ >+ # Check whether any conflicts were found. >+ if [ -n "$conflicting" ]; then >+ # What happens now depends on the user preferences. >+ if [ -n "$pExitOnConflict" ]; then >+ # The user has chosen to bail out when a conflict >+ # occurs. >+ log $ERR_CONFLICT "$task" >+ error $ERR_CONFLICT "The package <$targetPkgname> conflicts with the following packages:$IFS$conflicting" >+ elif [ -n "$pReplaceConflicts" ]; then >+ # The user has chosen that conflicting packages should >+ # be replaced as if they were explicitly listed for >+ # replacing. >+ conflicts= >+ for package in $conflicting; { >+ warn "The package <$package> conflicts with <$targetPkgname> and will be replaced." >+ removePackages="$removePackages${removePackages:+$IFS}$package" >+ origin="$(pkg_info -qo "$package")" >+ # The next line is just for prettier log output. >+ conflicts="${conflicts:+$conflicts, }<$package> ($origin)" >+ package="$(echo "$package" | getIdxEscape)" >+ replacePkgdep="$replacePkgdep -e 's|^@pkgdep $package\$|@pkgdep $targetPkgname|1'" >+ if [ "$origin" != "$targetOrigin" ]; then >+ replacePkgdep="$replacePkgdep -e 's|^@comment DEPORIGIN: $origin\$|@comment DEPORIGIN:$targetOrigin|1'" >+ fi >+ } >+ log 0 "Conflict <$targetPkgname> ($targetOrigin) remove package(s) $conflicts" >+ else >+ # The default action is to assume that the conflicting >+ # packages fulfill the required functionality. >+ conflicts= >+ for package in $conflicting; { >+ warn "The package <$targetPkgname> will not be installed in favour of <$package>, because they conflict." >+ origin="$(pkg_info -qo "$package")" >+ # Record the necessary substitutions. >+ # TODO: Later versions will have to store this >+ # for resume. >+ substituteDepends="${substituteDepends:+$substituteDepends$IFS}$targetPackage|$origin;$package" >+ # This is just for prettier log output. >+ conflicts="${conflicts:+$conflicts, }<$package> ($origin)" >+ } >+ # Log the conflict resolution. >+ log 0 "Conflict <$targetPkgname> ($targetOrigin) favour package(s) $conflicts" >+ # Skip to the next package. >+ return 0 >+ fi >+ fi >+ >+ # Backup packages. >+ mkdir -p "$packagebackup" >+ for package in $removePackages; { >+ printStatus "Backup <$package>." >+ pkg_create -b "$package" "$packagebackup/$package" >+ case $? in >+ 0) >+ # Everything went well. >+ ;; >+ 1) >+ # If this happens someone's been messing with >+ # the packages just milliseconds ago. >+ log $ERR_BACKUP_MISS "$task" >+ error $ERR_BACKUP_MISS "The backup of <$package> failed. The package is missing." >+ ;; >+ 2) >+ # Fortunately pkg_create backs up as much as >+ # as is possible. That the backup (and hence >+ # the present package) is incomplete is all >+ # the more reason to upgrade. >+ # I do not understand why portmaster is >+ # interactive in this case. >+ warn "Ignoring incomplete backup of <$package>." >+ ;; >+ *) >+ # Well, I've got no idea at all what else >+ # could go wrong. Too bad the return codes >+ # of pkg_create are not documented. >+ log $ERR_BACKUP_UNKNOWN "$task" >+ error $ERR_BACKUP_UNKNOWN "The backup of <$package> failed for unknown reasons." >+ ;; >+ esac >+ } >+ >+ # Block SIGINT (CTRL-C), because that would really wrack havoc upon >+ # the package database in the following section. >+ signal= >+ trap "signal=$ERR_USER" sigint >+ trap "signal=$ERR_TERM" sigterm >+ >+ # Delete packages. >+ requiredBy= >+ count=-1 >+ for package in $removePackages; { >+ printStatus "Delete <$package>." >+ # Remember +REQUIRED_BY contents for roll-back. >+ count=$(($count + 1)) >+ local "requiredBy$count" >+ setvar "requiredBy$count" "$(cat "$PKG_DBDIR/$package/+REQUIRED_BY" 2> /dev/null)" >+ # Remember +REQUIRED_BY contents for the new package. >+ requiredBy="${requiredBy:+$requiredBy$IFS}$(cat "$PKG_DBDIR/$package/+REQUIRED_BY" 2> /dev/null)" >+ # Finally delete the package. >+ pkg_delete -f "$package" >+ } >+ >+ # Update the package database. >+ printStatus "Update package database for <$targetPkgname>." >+ if [ -n "$replacePkgdep" ]; then >+ for file in $(find "$PKG_DBDIR" -name '+CONTENTS'); { >+ eval "sed -Ei '.$name' $replacePkgdep '$file'" >+ } >+ fi >+ >+ # If an old version of this package was favoured in a conflict, >+ # the substituteDepends list has to be changed. >+ substituteDepends="$(echo "$substituteDepends" | sed "s'\|$targetOrigin;.*'|$targetPackage'1")" >+ >+ # Try to install the new package. >+ printStatus "Install <$targetPkgname>." >+ if ! env PKG_PATH="$PACKAGES/All" pkg_add -f "$targetPkgname"; then >+ # Installation went wrong, roll back! >+ printStatus "Roll back changes for <$targetPkgname>." >+ for file in $(find "$PKG_DBDIR" -name "*.$name"); { >+ mv -f "$file" "${file%.$name}" >+ } >+ count=-1 >+ for package in $removePackages; { >+ # Restore package. >+ env PKG_PATH="$packagebackup" pkg_add -f "$package" >+ # Recover +REQUIRED_BY file. >+ count=$(($count + 1)) >+ eval "echo \"\$requiredBy$count\"" > "$PKG_DBDIR/$package/+REQUIRED_BY" >+ # Remove the backup if set. >+ test -n "$pNoBackup" && rm "$packagebackup/$package.tbz" >+ } >+ log $ERR_INSTALL "$task" >+ error $ERR_INSTALL "The installation of <$targetPkgname> failed." >+ fi >+ >+ # Add the +REQUIRED_BY contents of all deleted packages to the >+ # +REQUIRED_BY file of the new one. >+ requiredBy="$(echo "$(cat "$PKG_DBDIR/$targetPkgname/+REQUIRED_BY" 2> /dev/null)$IFS$requiredBy" | grep -vFx '' | sort -u)" >+ echo "$requiredBy" > "$PKG_DBDIR/$targetPkgname/+REQUIRED_BY" >+ >+ # Make dependency substitutions from conflict resolving. >+ substituteDepends "$targetPkgname" >+ >+ # Log successful completion of the task. >+ log 0 "$task" >+ >+ # Remove backups if set. >+ if [ -n "$pNoBackup" ]; then >+ for package in $removePackages; { >+ printStatus "Remove backup of <$package>." >+ rm "$packagebackup/$package.tbz" >+ } >+ fi >+ >+ # Remove package database backups. >+ # TODO: Later versions will instead store them to allow a rollback. >+ printStatus "Remove database backups for <$targetPkgname>." >+ find "$PKG_DBDIR" -name "*.$name" -exec rm \{\} \; >+ >+ # Clear the status line. >+ printStatus >+ echo "=> $task succeeded" >+ >+ # Bail out if SIGINT or SIGTERM were encountered. >+ if [ -n "$signal" ]; then >+ error $signal "The process was interrupted." >+ fi >+ >+ # Reactivate default signal handlers. >+ trap - sigint sigterm >+} >+ >+# >+# Identify the package by a given string. Outputs the origin of all matched >+# packages, as well as the package name of the newest available package. >+# The output is in the following shape: >+# <origin>;<package> >+# >+# The shell wildcards '*' and '?' are supported. >+# Origin and package names with wildcards are matched against installed >+# packages. Unambiguous package names and origins are matched against the >+# index. >+# >+# @param 1 >+# The package identifier to find matches for. >+# >+identifyPackage() { >+ local packages package mangledPackage rows matchingRows mangledRows >+ local origins origin guess escapedPkg >+ >+ # Check for wildcards. >+ guess= >+ if echo "$1" | grep -qE '\*|\?|\[.*]'; then >+ guess=1 >+ fi >+ package="$1" >+ >+ # Distuinguish between origins and packages. >+ case "$package" in >+ */*) >+ # An origin has been given. >+ if [ -n "$guess" ]; then >+ # Wildcards present, match against installed >+ # packages. >+ >+ # Get all matching packages. >+ packages="$(pkg_info -qO "$package")" >+ >+ # Convert for use in a regular expression. >+ package="$(echo "$package" | getIdxEscape -e 's/\*/[^|]*/g' -e 's/\?/[^|]/g')" >+ # Get rows matching the given package origin. >+ # This is a performance tweak, so the whole >+ # index will not have to be parsed in the >+ # following output loop. >+ rows="$(getIdxRows $IDX_ORIGIN "$idxports/$package")" >+ >+ # Output all matching packages. >+ for package in $packages; { >+ # Get the origin. >+ origin="$(pkg_info -qo "$package")" >+ >+ # Match this package origin against the >+ # previously filtered rows. >+ package="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" >+ # Get the package name of the newest >+ # package from the index. >+ package="$(getIdxColumn $IDX_PKG "$package")" >+ # Output origin/package pair. >+ echo "$origin;$package" >+ } >+ >+ # If no matches have been found, terminate. >+ if [ -z "$packages" ]; then >+ error $ERR_ARG "Package origin <$package> not matched by any installed package!" 1>&2 >+ fi >+ else >+ # There is an unambigious origin, match it >+ # against the index. >+ origin="$package" >+ # Get the index row. >+ rows="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" >+ # Get the package name column. >+ package="$(getIdxColumn $IDX_PKG "$rows")" >+ # Output origin/package pair, if a package for >+ # the given origin was found. >+ if [ -n "$package" ]; then >+ # Output origin/package pair. >+ echo "$origin;$package" >+ else >+ error $ERR_ARG "Package origin <$origin> not in index!" 1>&2 >+ fi >+ fi >+ ;; >+ *) >+ # A package name has been given. >+ if [ -n "$guess" ]; then >+ # Wildcards present, match against installed >+ # packages. >+ >+ # Get the origins of matching packages. >+ origins="$(pkg_info -qo "$package")" >+ >+ # Prepare the package name for use in a >+ # regular expression. >+ package="$(echo "$package" | getIdxEscape -e 's/\*/[^|]*/g' -e 's/\?/[^|]/g')" >+ # Get rows matching the given package name. >+ # This is a performance tweak, so the whole >+ # index will not have to be parsed in the >+ # following output loop. >+ rows="$(getIdxRows $IDX_PKG "$package")" >+ # Output all matching packages. >+ for origin in $origins; { >+ # Get the index row for this origin. >+ package="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" >+ # Get the latest package name from the >+ # index. >+ package="$(getIdxColumn $IDX_PKG "$package")" >+ # Output origin/package pair. >+ echo "$origin;$package" >+ } >+ >+ # If no matches have been found, terminate. >+ if [ -z "$origins" ]; then >+ error $ERR_ARG "Package identifier <$package> not matched!" 1>&2 >+ fi >+ else >+ # A package name without wildcards has been >+ # given. This is expected to either be an exact >+ # package name or a LATEST_LINK name. >+ >+ # TODO: This would be much better if >+ # LATEST_LINK was known. This is information >+ # simply missing in the index. >+ # To make up for this some guessing is done in >+ # case of no matches or more than one match. >+ # But this fails for apache13 and probably >+ # other packages as well. >+ >+ # First try whether it is the current version >+ # of a package. >+ origin="$(pkg_info -qo "$package" 2> /dev/null)" >+ if [ -n "$origin" ]; then >+ # Get the matching index rows. >+ rows="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" >+ fi >+ >+ # If it's not a current version, match against >+ # the index. >+ if [ -z "$rows" ]; then >+ # Get the matching rows. This should be >+ # only one, but it won't be for ports >+ # that define a proprietary LATEST_LINK. >+ escapedPkg="$(echo "$package" | getIdxEscape)" >+ rows="$(getIdxRows $IDX_PKG "$escapedPkg(-[^-]+)?")" >+ fi >+ >+ # No match, start some guessing. >+ # This fails for packages with a version tail, >+ # which is just what is wanted. >+ if [ -z "$rows" ]; then >+ # Assume this is a LATEST_LINK kind >+ # package name and remove the trailing >+ # numbers. >+ mangledPackage="$(echo "$package" | sed -E 's/[0-9]+$//1')" >+ # Get the matching rows, this is likely >+ # to be too many (i.e. more than one). >+ rows="$(getIdxRows $IDX_PKG "$mangledPackage-[^-]+")" >+ fi >+ >+ # If there is more than one matching row, >+ # try to match against the origin. >+ if [ "$(($(echo "$rows" | wc -l)))" -gt "1" ]; then >+ # Match against the origin. >+ rows="$(getIdxRows $IDX_ORIGIN "[^|]*/$package" "$rows")" >+ >+ # If there is still more than one >+ # match, match against the origins >+ # of existing packages. >+ if [ "$(($(echo "$rows" | wc -l)))" -gt "1" ]; then >+ for origin in $(getIdxColumn $IDX_ORIGIN "$rows"); { >+ test -n "$(pkg_info -qO "$origin")" \ >+ && matchingRows="$matchingRows${matchingRows:+$IFS}$(getIdxRowsEscaped $IDX_ORIGIN "$origin" "$rows")" >+ } >+ rows="$matchingRows" >+ fi >+ >+ # Either a single origin is matched or >+ # it's time to bail out and give up. >+ if [ "$(($(echo "$rows" | wc -l)))" -ne "1" ]; then >+ # The wrong amount of matches >+ # has occured. Bail out. >+ error $ERR_ARG "Package identifier <$package> not unambiguously matched!" 1>&2 >+ fi >+ fi >+ >+ # Output if a package has been matched. >+ if [ -n "$rows" ]; then >+ # Get the origin of the given package. >+ origin="$(getIdxColumn $IDX_ORIGIN "$rows")" >+ # Geth the package name. >+ package="$(getIdxColumn $IDX_PKG "$rows")" >+ # Output origin/package pair. >+ echo "${origin#$idxports/};$package" >+ else >+ error $ERR_ARG "Package identifier <$package> not in index!" 1>&2 >+ fi >+ fi >+ ;; >+ esac >+} >+ >+# >+# Prints the parameter list and terminates the program. >+# >+printHelp() { >+ printf "$name v$version >+usage: >+ $name -h >+ $name -a [-b] [-bcCdfFlnpvX] [-o new existing] [update] [install] >+ $name [-bcCdfFlnpvX] [-r [-r]] [-R [-R]] [-o new existing] >+ %${#name}s [update] [install]\n" '' >+ exit 0 >+} >+ >+# >+# Parse the command line parameters. >+# >+# @param upgrade >+# A list of packages to upgrade. >+# @param depth >+# This is used by the function to store the recursion depth and >+# should be unset when calling it. >+# @param origin >+# This is used by the function across differtent recursion depths to >+# remember whether a package origin is expected. >+# @param pAll >+# Is set if all packages should be update. >+# @param pNoBackup >+# Is set if backups could not be fetched. >+# @param pClean >+# Is set to turn off status messages. >+# @param pReplaceConflicts >+# Is set to replace conflicting packages with new ones instead of >+# leaving them alone. >+# @param pExitOnConflict >+# Is set to stop the program if a conflict is encountered. >+# @param pForce >+# Is set to force the update of packages that are not really updated. >+# @param pFetchOnly >+# Is set to only fetch packages instead of installing/upgrading them. >+# @param pInteractive >+# TODO: Reserved for future versions (resume/roll-back). >+# @param pJobs >+# TODO: Reserved for future versions (pkg_libchk tests). >+# @param pListDiscarded >+# Is set to activate the listing of packages that are ignored because >+# they are not set in the INDEX. >+# @param pNoActions >+# Is set if no actions should be performed but a list of what would have >+# been done should get printed. >+# @param pNoLogging >+# Turn off logging. >+# @param pParanoid >+# Is set to activate cyclic dependency checks. >+# @param pRecursive >+# Is set to activate updating of dependencies. >+# @param pMoreRecursive >+# Is set to activate updating of dependencies of depending packages. >+# @param pUpwardRecursive >+# Is set to activate updating of depending packages. >+# @param pMoreUpwardRecursive >+# Is set to activate updating of packages depending on dependencies. >+# @param pVerbose >+# Is set to activate informative output. >+# >+readParams() { >+ local arg package escapedPkg depth >+ # Store the recursion depth. Note that counting down is dealt with >+ # by making depth local. >+ depth=$((${depth:--1} + 1)) >+ >+ # This is used to remember whether the next parameter should >+ # be a replacing package or a packge to be replaced. >+ origin=${origin:-0} >+ >+ for arg { >+ # >+ # Handle package replacements. >+ # >+ if [ $origin -eq 1 ]; then >+ # Store the replacement. >+ package="$(identifyPackage "$arg")" || exit $? >+ if [ -z "$package" -o "$(($(echo "$package" | wc -l)))" -ne "1" ]; then >+ error $ERR_ARG "The package identifier <$arg> is not unambiguous." >+ fi >+ upgrade="$upgrade${upgrade:+$IFS}$package" >+ replace="$replace${replace:+$IFS}$package" >+ origin=2 >+ # Request the download. >+ downloadManagerMsgRequest "$package" >+ continue >+ fi >+ if [ $origin -eq 2 ]; then >+ # Store what to replace. >+ # This is taken from the package database not the index. >+ >+ case "$arg" in >+ */*) >+ # Assume arg is an origin. >+ package="$(pkg_info -qO "$arg")" >+ package="$(pkg_info -qo "$package" 2> /dev/null);$package" >+ ;; >+ *) >+ # Assume arg is a package identifier. >+ package="$(pkg_info -qo "$arg" 2> /dev/null);$(pkg_info -E "$arg" 2> /dev/null)" >+ >+ # Maybe arg is a package identifier >+ # without a version tail. >+ if [ "$package" = ";" ]; then >+ package="$(pkg_info -qo "$arg-*" 2> /dev/null);$(pkg_info -E "$arg-*" 2> /dev/null)" >+ fi >+ ;; >+ esac >+ >+ # Arg is not installed. >+ if [ "$package" = ";" ]; then >+ error $ERR_ARG "The package <$arg> is not installed and thus cannot be replaced." >+ fi >+ # It appears arg is an identifier that is >+ # not unambiguous. >+ if [ "$(($(echo "$package" | wc -l)))" -ne "1" ]; then >+ error $ERR_ARG "The package identifier <$arg> is not unambiguous." >+ fi >+ # A package can only be replaced once. >+ escapedPkg="$(echo "$package" | getIdxEscape)" >+ if echo "$replace" | grep -qEx ".*\|$escapedPkg"; then >+ error $ERR_ARG "The package <$arg> is already listed for replacement." >+ fi >+ replace="$replace|$package" >+ origin=0 >+ continue >+ fi >+ >+ # >+ # Identify arguments. >+ # >+ case "$arg" in >+ "-a" | "--all") >+ pAll=1 >+ if [ -n "$pRecursive" ]; then >+ error $ERR_ARG "Recursiveness has no effect, because all packages are already selected for processing." >+ fi >+ if [ -n "$pUpwardRecursive" ]; then >+ error $ERR_ARG "Upward recursiveness has no effect, because all packages are already selected for processing." >+ fi >+ ;; >+ "-b" | "--no-backup") >+ pNoBackup=1 >+ ;; >+ "-c" | "--clean") >+ pClean=-c >+ ;; >+ "-C" | "--replace-conflicts") >+ if [ -n "$pExitOnConflict" ]; then >+ error $ERR_ARG "The 'replace conflicts' and 'exit on conflict' modes are mutually exclusive." >+ fi >+ pReplaceConflicts=1 >+ ;; >+ "-d" | "--list-discarded") >+ pListDiscarded=1 >+ ;; >+ "-f" | "--force") >+ pForce=1 >+ ;; >+ "-F" | "--fetch-only") >+ if [ -n "$pNoActions" ]; then >+ error $ERR_ARG "The 'no actions' and 'fetch only' modes are mutually exclusive." >+ fi >+ pFetchOnly=1 >+ ;; >+ "-h" | "--help") >+ printHelp >+ ;; >+ "-i" | "--interactive") >+ # TODO: not yet used >+ pInteractive=1 >+ ;; >+ -j* | --jobs*) >+ # TODO: not yet used >+ pJobs="$arg" >+ if ! pkg_libchk "$pJobs" DUMMY/DUMMY 1>&2; then >+ exit $ERR_ARG >+ fi >+ ;; >+ "-l" | "--no-logging") >+ pNoLogging=1 >+ ;; >+ "-n" | "--no-actions") >+ if [ -n "$pFetchOnly" ]; then >+ error $ERR_ARG "The 'no actions' and 'fetch only' modes are mutually exclusive." >+ fi >+ pNoActions=1 >+ ;; >+ "-o" | "--origin") >+ # Make sure the local index copy is up to date. >+ getIndex >+ origin=1 >+ ;; >+ "-p" | "--paranoid") >+ pParanoid=1 >+ ;; >+ "-r" | "--recursive") >+ if [ -n "$pMoreRecursive" ]; then >+ error $ERR_ARG "There are only two levels of recursiveness." >+ elif [ -n "$pRecursive" ]; then >+ pMoreRecursive=1 >+ else >+ pRecursive=1 >+ fi >+ if [ -n "$pAll" ]; then >+ error $ERR_ARG "Recursiveness has no effect, because all packages are already selected for processing." >+ fi >+ ;; >+ "-R" | "--upward-recursive") >+ if [ -n "$pMoreUpwardRecursive" ]; then >+ error $ERR_ARG "There are only two levels of upward recursiveness." >+ elif [ -n "$pUpwardRecursive" ]; then >+ pMoreUpwardRecursive=1 >+ else >+ pUpwardRecursive=1 >+ fi >+ if [ -n "$pAll" ]; then >+ error $ERR_ARG "Upward recursiveness has no effect, because all packages are already selected for processing." >+ fi >+ ;; >+ "-v" | "--verbose") >+ pVerbose=-v >+ ;; >+ "-X" | "--exit-on-conflict") >+ if [ -n "$pReplaceConflicts" ]; then >+ error $ERR_ARG "The 'exit on conflict' and 'replace conflicts' modes are mutually exclusive." >+ fi >+ pExitOnConflict=1 >+ ;; >+ -? | --*) >+ error $ERR_ARG "Unknown parameter \"$arg\"." >+ ;; >+ -*) >+ # Split parmeters. >+ readParams "${arg%%${arg#-?}}" "-${arg#-?}" >+ ;; >+ *) >+ # Make sure the local index copy is up to date. >+ getIndex >+ # Add package to the list of packages to >+ # upgrade/install. >+ package="$(identifyPackage "$arg")" || exit $? >+ upgrade="$upgrade${upgrade:+$IFS}$package" >+ # Request the download. >+ downloadManagerMsgRequest "$package" >+ ;; >+ esac >+ } >+ >+ # >+ # Only perform the following steps if this is the root call >+ # to this function (recursion depth = 0). >+ # >+ if [ $depth -eq 0 ]; then >+ # >+ # Deal with missing parameters. >+ # >+ if [ $origin -eq 1 ]; then >+ error $ERR_ARG "Incomplete parameters, missing origin." >+ fi >+ if [ $origin -eq 2 ]; then >+ error $ERR_ARG "Incomplete parameters, missing package to replace." >+ fi >+ >+ # >+ # Deal with invalid levels of recursiveness. >+ # >+ if [ -n "$pMoreRecursive" -a -z "$pUpwardRecursive" ]; then >+ error $ERR_ARG "Thorough recursiveness can only be used in conjunction with upwards recursiveness." >+ fi >+ >+ # >+ # Remove duplicates in the list of packages to upgrade. >+ # >+ upgrade="$(echo "$upgrade" | sort -u)" >+ # Reset global variables. >+ origin= >+ fi >+} >+ >+# >+# Reads all the required +CONTENTS information from a package. >+# The information is stored in variables. >+# >+# @param 1 >+# The name of the package file. >+# @param pkgname >+# The name of the package. >+# @param origin >+# The origin of the package. >+# @param depends >+# The dependencies of the package in the format "<origin>;<package>", >+# in reverse order. >+# @param conflicts >+# A list of regular expressions that can be used to identify conflicting >+# packages. >+# >+readContents() { >+ local contents line format >+ contents="$(tar -xOf "$1" '+CONTENTS')" >+ format= >+ >+ pkgname= >+ origin= >+ depends= >+ conflicts= >+ for line in $contents; { >+ case "$line" in >+ @name\ *) >+ pkgname="${line#@name }" >+ ;; >+ @pkgdep\ *) >+ depends=";${line#@pkgdep }${depends:+$IFS}$depends" >+ ;; >+ @comment\ *) >+ line="${line#@comment }" >+ case "$line" in >+ DEPORIGIN:*) >+ depends="${line#*:}$depends" >+ ;; >+ PKG_FORMAT_REVISION:*) >+ format="${line#*:}" >+ ;; >+ ORIGIN:*) >+ origin="${line#*:}" >+ ;; >+ esac >+ ;; >+ @conflicts\ *) >+ conflicts="${conflicts:+$conflicts$IFS}${line#@conflicts }" >+ ;; >+ esac >+ } >+ >+ if [ "$format" != "1.1" ]; then >+ error $ERR_PACKAGE_FORMAT "Unknown package format in <$1>, bailing out!" >+ fi >+ >+ return 0 >+} >+ >+# >+# Starts a download manager that can be instructed through a queue. >+# A process that wants to know what's going on with the download manager >+# can simply read from the queue as well. >+# >+# The download manager keeps as many downloads running as there are >+# PACKAGESITE_MIRRORS. Should a download fail, it is retried as soon >+# as no untried downloads remain. Every download is only retried once. >+# A download is never attempted from the master server. >+# >+# @param queueMessages >+# The queue to create and read from. >+# @param packagerepos >+# The location of the remote package repository (derived from >+# PACKAGESITE). If this is identical with the local repository, >+# the download manager will not be started. >+# @param pNoActions >+# If set, the download manager will not be started. >+# >+downloadManager() { >+ # No actions mode, this includes no downloads. >+ test -n "$pNoActions" && return 0 >+ >+ # Packages are locally available, no downloads. >+ test "$PACKAGES" = "$packagerepos" && return 0 >+ >+ verbose "Start the download manager." >+ >+ # Initialize the queue. >+ rm "$queueMessages" 2> /dev/null >+ touch "$queueMessages" >+ >+ # >+ # The following block is forked away. >+ # Note that all variable assignments happen in a separate process >+ # and hence have no effect on the outside. >+ # >+ ( >+ # Remove the queue when exiting and get rid of pending jobs. >+ trap " >+ kill \$(jobs -ls) > /dev/null 2>&1 >+ rm '$queueMessages' 2> /dev/null >+ exit >+ " EXIT sigint sigterm >+ >+ # The jobs yet to be done. >+ jobs= >+ # The available mirrors. >+ mirrors="$PACKAGESITE_MIRRORS" >+ # The jobs that should be retried. >+ retry= >+ # The jobs that have been retried. >+ retried= >+ # The last line read from the socket. >+ line= >+ >+ # Keep on running as long as the father process is around. >+ # Note that this while loop has the message queue as stdin. >+ while kill -0 "$pid" 2> /dev/null; do >+ # Check for a message in the queue. >+ # There is nothing to be done, if there was no message, >+ # none the less it times out to allow the terminal >+ # to catch signals. >+ read -t 2 line >+ # Process messages. >+ case "$line" in >+ finished:*) >+ # A download has been finished. >+ # Add the mirror that was used to >+ # the list of available mirrors. >+ mirror="${line#finished:}" >+ mirror="${mirror%;*}" >+ mirrors="${mirrors:+$mirrors$IFS}$mirror" >+ ;; >+ retry:*) >+ # A download was not finished >+ # successfuly. >+ mirror="${line#retry:}" >+ job="${mirror##*;}" >+ mirror="${mirror%;*}" >+ if echo "$retried" | grep -qFx "$job"; then >+ # If this package has already >+ # had a retry, mark it as >+ # finished to hand it over >+ # to the package validation >+ # that can fetch from the >+ # master server. >+ downloadManagerMsgFinished "$mirror" "$job" >+ else >+ # The first retry request. >+ # Free the mirror and list >+ # the package for retry. >+ mirrors="${mirrors:+$mirrors$IFS}$mirror" >+ retry="${retry:+$retry$IFS}$job" >+ fi >+ ;; >+ request:*) >+ # Append requested downloads to the >+ # list of available jobs. >+ jobs="${jobs:+$jobs$IFS}${line#request:}" >+ ;; >+ exit) >+ # The download manager has been told >+ # to terminate. >+ break >+ ;; >+ esac >+ # Delete the line, so it cannot be read again in the >+ # next iteration, if reading from the queue has >+ # timed out. >+ line= >+ >+ # If any mirrors are available and there are jobs >+ # in the queue, now is the time to dispatch them. >+ while [ -n "$jobs" -a -n "$mirrors" ]; do >+ mirror="${mirrors%%$IFS*}" >+ mirrors="${mirrors#$mirror}" >+ mirrors="${mirrors#$IFS}" >+ job="${jobs%%$IFS*}" >+ jobs="${jobs#$job}" >+ jobs="${jobs#$IFS}" >+ downloadManagerFetch "$mirror" "$job" & >+ done >+ >+ # If we have run out of jobs, give the retry stuff. >+ # a try. >+ while [ -n "$retry" -a -n "$mirrors" ]; do >+ mirror="${mirrors%%$IFS*}" >+ mirrors="${mirrors#$mirror}" >+ mirrors="${mirrors#$IFS}" >+ job="${retry%%$IFS*}" >+ retry="${retry#$job}" >+ retry="${retry#$IFS}" >+ # Remember that this job has been retried. >+ retried="${retried:+$retried$IFS}$job" >+ downloadManagerFetch "$mirror" "$job" & >+ done >+ done < "$queueMessages" >+ ) & >+} >+ >+# >+# This is forked off by the download manager to download a package from >+# a mirror. >+# If the package is already present a download is not attempted. >+# >+# @param 1 >+# The mirror to download from. >+# @param 2 >+# The name of the package to download. >+# >+downloadManagerFetch() { >+ # Get rid of pending jobs. >+ trap " >+ kill \$(jobs -ls) > /dev/null 2>&1 >+ exit >+ " EXIT sigint sigterm >+ >+ # Only do something if the package is not present. >+ if ! [ -e "$PACKAGES/All/$2.tbz" ]; then >+ # Create the download location. >+ mkdir -p "$PACKAGES/All" 2> /dev/null >+ >+ # Attempt download from mirror. >+ # This is forked off, to allow the shell to catch signals. >+ fetch -qmo "$PACKAGES/All/$2.tbz" "${1%/*?}/All/$2.tbz" > /dev/null 2>&1 & >+ if ! wait $!; then >+ # Release the mirror and mark the package for a retry. >+ downloadManagerMsgRetry "$1" "$2" >+ return 0 >+ fi >+ fi >+ >+ # Release the mirror and mark package finished. >+ downloadManagerMsgFinished "$1" "$2" >+} >+ >+# >+# Tells the download manager, that a download was unsuccessful. >+# >+# @param 1 >+# The mirror that was used. >+# @param 2 >+# The name of the package that was not downloaded. >+# @param queueMessages >+# The message queue to the download manager. >+# >+downloadManagerMsgRetry() { >+ # Do not send anything without a queue. >+ test ! -e "$queueMessages" && return 0 >+ >+ lockf -k "$queueMessages" sh -c "echo 'retry:$1;$2' >> '$queueMessages'" >+} >+ >+# >+# Tells the download manager, that a download has been finished. >+# >+# @param 1 >+# The mirror that was used. >+# @param 2 >+# The name of the downloaded package. >+# @param queueMessages >+# The message queue to the download manager. >+# >+downloadManagerMsgFinished() { >+ # Do not send anything without a queue. >+ test ! -e "$queueMessages" && return 0 >+ >+ lockf -k "$queueMessages" sh -c "echo 'finished:$1;$2' >> '$queueMessages'" >+} >+ >+# >+# Requests the download of packages from the download manager. >+# >+# @param 1 >+# A list of packages for download. >+# @param queueMessages >+# The message queue to the download manager. >+# >+downloadManagerMsgRequest() { >+ # Do not send anything without a queue. >+ test ! -e "$queueMessages" && return 0 >+ >+ local request >+ for request in $1; { >+ lockf -k "$queueMessages" sh -c "echo 'request:${request#*;}' >> '$queueMessages'" >+ } >+} >+ >+# >+# Instructs the download manager to terminate. >+# >+# @param queueMessages >+# The message queue to the download manager. >+# >+downloadManagerMsgExit() { >+ # Do not send anything without a queue. >+ test ! -e "$queueMessages" && return 0 >+ >+ lockf -k "$queueMessages" sh -c "echo 'exit' >> '$queueMessages'" >+} >+ >+# >+# Validates a single package. Validation means it checks whether a package >+# is a complete tar archive. Damaged or missing packages will be (re)downloaded >+# from the master server (the one named by PACKAGESITE). >+# If the package is a valid tar archive the +CONTENTS file will be checked, >+# as well. >+# >+# @param 1 >+# The name of the package to validate. >+# @param packagerepos >+# The location of the remote package collection. This is derived from >+# PACKAGESITE. >+# @param pending >+# The list of pending packages. >+# @return >+# Return 0 on success. >+# >+validatePackage() { >+ local package >+ package="$1.tbz" >+ >+ # Check whether the package is intact and present. >+ if ! tar -tf "$PACKAGES/All/$package" > /dev/null 2>&1; then >+ # If the package repository and the local package collection >+ # are identical, there's no chance to get the package if it's >+ # not already there. >+ if [ "$PACKAGES" = "$packagerepos" ]; then >+ error $ERR_FETCH "The package <$package> is not present." >+ fi >+ >+ # Clean up whatever crap is there. >+ rm "$PACKAGES/All/$package" 2> /dev/null >+ >+ # Try to get the package from the master server. >+ fetch -mo "$PACKAGES/All/$package" "$packagerepos/All/$package" >+ >+ # Check whether the package is present. >+ if ! [ -e "$PACKAGES/All/$package" ]; then >+ error $ERR_FETCH "The package <$package> could not be fetched." >+ fi >+ >+ # Check whether the package is a valid tar archive. >+ if ! tar -tf "$PACKAGES/All/$package" > /dev/null 2>&1; then >+ error $ERR_FETCH "The package <$package> could not be read." >+ fi >+ fi >+ >+ # Check whether we can read the package +CONTENTS format. >+ readContents "$PACKAGES/All/$package" >+ >+ # Remove this package from the list of pending packages. >+ pending="$(echo "$pending" | grep -vFx "$1")" >+ >+ # The package is present and intact. >+ return 0 >+} >+ >+# >+# Let's get it on! The declarative part is finally over. >+# >+ >+# Ignore some signals that should not occur. >+trap 'warn "Discard signal SIGHUP."' sighup >+trap 'warn "Discard signal SIGUSR1."' sigusr1 >+trap 'warn "Discard signal SIGUSR2."' sigusr2 >+ >+# >+# Parse command line parameters. >+# >+readParams "$@" >+ >+# Make sure the index is available for the following operations. >+getIndex >+ >+# >+# Populate the list of packages out of sync with the index. >+# >+pkgAll >+ >+# >+# Perform dependency checking. >+# >+pkgDependencies >+ >+# >+# Sort packages by their dependencies. >+# >+pkgSort >+ >+# >+# Display tasks. >+# >+pkgList >+ >+# >+# Download packages. >+# >+pkgDownload >+ >+# >+# Upgrade packages. >+# >+pkgUpgrade >+ >+exit 0 >diff -urN /usr/ports/sysutils/bsdadminscripts.old/files/uma.in /usr/ports/sysutils/bsdadminscripts/files/uma.in >--- /usr/ports/sysutils/bsdadminscripts.old/files/uma.in 1970-01-01 01:00:00.000000000 +0100 >+++ /usr/ports/sysutils/bsdadminscripts/files/uma.in 2014-08-26 11:18:27.000000000 +0200 >@@ -0,0 +1,436 @@ >+#!/bin/sh -f >+# >+# Copyright (c) 2009 >+# Dominic Fandrey <kamikaze@bsdforen.de> >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# >+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR >+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES >+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. >+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, >+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT >+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF >+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+# >+ >+readonly version=1.1.1 >+readonly name=uma >+ >+# Return value. >+errno=0 >+# Allow things to fail properly by ignoring SIGINT in the main process. >+trap '' int >+ >+# Used to activate verbose output. >+verbose= >+# Will be set if files are locally available. >+local= >+ >+vardir="%%VAR%%" >+lock="$vardir/run/$name.lock" >+lockpid="$vardir/run/$name.pid" >+identpid="$vardir/run/$name.ident.pid" >+conf="%%PREFIX%%/etc/$name.conf" >+ >+# Use line breaks as a delimiter. >+IFS=' >+' >+# Timezone UTC for age comparisons. >+export TZ=UTC >+ >+# The bit position of errors. >+readonly ERR_LOCK=0 >+readonly ERR_ARG=1 >+readonly ERR_FETCH_PORTS=2 >+readonly ERR_FETCH_VULNDB=3 >+readonly ERR_FETCH_INDEX=4 >+readonly ERR_EXTRACT_PORTS=5 >+readonly ERR_UPDATE_PORTS=6 >+ >+# >+# Get environment variables. >+# >+ >+# Load the configuration file if present. >+if [ -e "$conf" ]; then >+ . "$conf" >+fi >+ >+# Local index location. >+: ${PKG_INDEX="$vardir/db/uma/FTPINDEX"} >+: ${FTP_TIMEOUT=60} >+ >+# Logic from src/usr.sbin/pkg_install/add/main.c, plus the possibility to >+# override the architecture with ARCH. >+: ${PACKAGEROOT="ftp://ftp.freebsd.org"} >+: ${ARCH="$(uname -m)"} >+branch="$(uname -r | tr '[:upper:]' '[:lower:]')" >+number="${branch%%.*}" >+branch="${branch##*-}" >+case "$branch" in >+ release) >+ branch=$number-$branch >+ ;; >+ stable|current) >+ branch=${number%%.*}-$branch >+ ;; >+ *) >+ # Fallback to stable for prerelease and the like. >+ branch=${number%%.*}-stable >+ ;; >+esac >+: ${BRANCH=$branch} >+: ${PACKAGESITE="$PACKAGEROOT/pub/FreeBSD/ports/$ARCH/packages-$BRANCH/Latest"} >+packagetree="${PACKAGESITE%/*?}" >+ftp="${PACKAGESITE#*://}" >+ftp="${ftp%%/*}" >+ >+# >+# Generate PACKAGESITE_MIRRORS if only PACKAGEROOT_MIRRORS are given. >+# Note that PACKAGEROOT_MIRRORS and PACKAGESITE_MIRRORS are supposed to be >+# a ";" or line feed separated list. Semicolons will be converted to line >+# feeds in any case. >+# >+ >+# Set PACKAGEROOT_MIRRORS if not set. >+if [ -z "$PACKAGEROOT_MIRRORS" ]; then >+ PACKAGEROOT_MIRRORS= >+ for i in $(jot 14); { >+ PACKAGEROOT_MIRRORS="${PACKAGEROOT_MIRRORS:+$PACKAGEROOT_MIRRORS$IFS}ftp://ftp$i.FreeBSD.org" >+ } >+fi >+ >+# Convert semicolon in PACKAGEROOT_MIRRORS. >+PACKAGEROOT_MIRRORS="$(echo "$PACKAGEROOT_MIRRORS" | sed "s/;/\\$IFS/g")" >+# Build PACKAGESITE_MIRRORS. >+if [ -z "${PACKAGESITE_MIRRORS}" ]; then >+ PACKAGESITE_MIRRORS= >+ for MIRROR in $PACKAGEROOT_MIRRORS; { >+ PACKAGESITE_MIRRORS="${PACKAGESITE_MIRRORS:+$PACKAGESITE_MIRRORS$IFS}$MIRROR/pub/FreeBSD/ports/$ARCH/packages-$BRANCH/Latest" >+ } >+fi >+# Convert semicolon in PACKAGESITE_MIRRORS. >+PACKAGESITE_MIRRORS="$(echo "$PACKAGESITE_MIRRORS" | sed "s/;/\\$IFS/g")" >+ >+# Remove duplicates. >+PACKAGEROOT_MIRRORS="$(echo "$PACKAGEROOT_MIRRORS" | sort -u)" >+PACKAGESITE_MIRRORS="$(echo "$PACKAGESITE_MIRRORS" | sort -u)" >+ >+# Determine portsdir >+portsdir=$(make -V PORTSDIR -f /usr/share/mk/bsd.port.mk 2> /dev/null) >+portsdir="${portsdir:-%%PORTS%%}" >+ >+export ARCH BRANCH PKG_INDEX FTP_TIMEOUT PACKAGEROOT PACKAGESITE >+export PACKAGEROOT_MIRRORS PACKAGESITE_MIRRORS >+ >+# >+# This function is called by a trap when the script exits in verbose mode. >+# It reads errno to construct error messages. >+# >+# @param errno >+# The exit status of the script. >+# >+verbose() { >+ if [ $(($errno >> $ERR_LOCK & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_LOCK))): Lock owned by someone else." >+ fi >+ if [ $(($errno >> $ERR_ARG & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_ARG))): An unknown parameter was supplied." >+ fi >+ if [ $(($errno >> $ERR_FETCH_PORTS & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_FETCH_PORTS))): Fetching the ports tree failed." >+ fi >+ if [ $(($errno >> $ERR_FETCH_VULNDB & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_FETCH_VULNDB))): Fetching security database failed." >+ fi >+ if [ $(($errno >> $ERR_FETCH_INDEX & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_FETCH_INDEX))): Fetching remote INDEX failed." >+ fi >+ if [ $(($errno >> $ERR_EXTRACT_PORTS & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_EXTRACT_PORTS))): Extracting the ports tree failed." >+ fi >+ if [ $(($errno >> $ERR_UPDATE_PORTS & 1)) -eq 1 ]; then >+ echo "ERROR($((1 << $ERR_UPDATE_PORTS))): Updating the ports tree failed." >+ fi >+} >+ >+# >+# This function spawns a process that takes over a lock. >+# >+# @param pid >+# The PID of the process that requested the lock. >+# @param lock >+# The location of the lock file. >+# @param lockpid >+# The location of the PID file for the lock holding process. >+# >+secureLock() { >+ lockf "$lock" sh -c " >+ trap 'exit 0' term >+ echo '$pid' > '$lock' >+ echo \"\$\$\" > '$lockpid' >+ trap 'rm \"$lockpid\"; exit 0' EXIT >+ while kill -0 '$pid' 2> /dev/null; do >+ sleep 2 >+ done >+ " 2> /dev/null & >+} >+ >+# >+# Checks whether the currently requesting process holds the lock. >+# >+# @param pid >+# The PID of the process that requested the lock. >+# @param lock >+# The location of the lock file. >+# @return >+# Returns 0 if the lock is held for the requesting process or 1 >+# if the lock is missing or owned by another process. >+# >+hasLock() { >+ test "$pid" -eq "$(cat "$lock" 2> /dev/null)" 2> /dev/null >+ return $? >+} >+ >+# >+# Creates a lock for the requesting process. >+# >+# @param pid >+# The PID of the process that requested the lock. >+# @param lock >+# The location of the lock file. >+# @param lockpid >+# The location of the PID file for the lock holding process. >+# @param portsdir >+# The location of the FreeBSD ports tree. >+# @return >+# Returns 0 on success, 1 on failure. >+# >+lock() { >+ local location >+ >+ # The requestor already holds the lock. >+ hasLock && return 0 >+ >+ # The process requesting the lock does not exist. >+ kill -0 "$pid" 2> /dev/null || return 1 $(errno=1) >+ >+ # Follow symlinks >+ location="$(pwd)" >+ if cd "$portsdir" && portsdir="$(pwd -P)"; then >+ # Portsdir exists, so we can test for make activity. This >+ # does not cover all cases, but it covers a lot. >+ if fstat "$portsdir" | awk '{print $2}' | grep -q make; then >+ errno=1 >+ return 1 >+ fi >+ fi >+ cd "$location" >+ >+ # Try acquiring the lock. >+ lockf -st 0 "$lock" "$0" secure $pid 2> /dev/null || return 1 $(errno=1) >+ # Wait until the locking process is properly set up. >+ while ! [ -e "$lockpid" -a -e "$lock" ]; do >+ sleep 0.1 >+ done >+ return 0 >+} >+ >+# >+# Frees a lock unless it is held for another process than the requestor. >+# >+# @param lock >+# The location of the lock file. >+# @param lockpid >+# The location of the PID file for the lock holding process. >+# @return >+# Returns 0 on success, 1 on failure. >+# >+unlock() { >+ if hasLock; then >+ # Free the lock. >+ kill -TERM "$(cat "$lockpid")" >+ # Wait for the locking process to clean up. >+ while [ -e "$lockpid" -o -e "$lock" ]; do >+ sleep 0.1 >+ done >+ return 0 >+ else >+ errno=1 >+ return 1 >+ fi >+} >+ >+# >+# Prints the command and available parameters. >+# >+# @param name >+# The name of the script. >+# @param version >+# The version of the script. >+# >+printHelp() { >+ echo "$name v$version >+usage: >+ $name [-hv] [pid] [fetch] [extract] [update] [...] >+ $name [-hv] [pid] fetch [ports] [audit] [ftpindex] >+ $name [-hv] [pid] extract [ports] >+ $name [-hv] [pid] update [ports] >+ $name [-hv] lock [pid] >+ $name [-hv] unlock [pid]" >+} >+ >+# >+# Reads the parameters and creates variables that indicates the presence >+# of these parameters. >+# >+# The last numeric value is treated as the requestor PID. It also deals >+# >+# @param @ >+# All parameters to process. >+# @param verbose >+# Set to 1 if verbose mode is activated. >+# @param cmd_* >+# Set by this function to indicate the presence of a parameter. >+# >+readParams() { >+ local flag >+ for flag; { >+ # A numerical parameter is the PID. >+ if [ "$flag" -eq "$flag" ] 2> /dev/null; then >+ pid="${flag}" >+ continue >+ fi >+ >+ # Activate verbose mode for -v. >+ case "$flag" in >+ -v | --verbose) >+ trap 'verbose 1>&2' EXIT >+ verbose=1 >+ continue >+ ;; >+ -h | --help) >+ printHelp >+ continue >+ ;; >+ -? | --*) >+ errno=$((1 << $ERR_ARG)) >+ exit $errno >+ ;; >+ -*) >+ # Split parameters. >+ readParams "${flag%${flag#-?}}" "-${flag#-?}" >+ continue >+ ;; >+ esac >+ >+ # If the variable is not predefined, the command is unknown. >+ if eval "test -n \"\${cmd_$flag=1}\""; then >+ errno=$((1 << $ERR_ARG)) >+ exit $errno >+ fi >+ setvar "cmd_$flag" 1 >+ } >+} >+ >+pid="$$" >+cmd_lock= >+cmd_unlock= >+cmd_secure= >+cmd_env= >+cmd_fetch= >+cmd_extract= >+cmd_update= >+cmd_ports= >+cmd_audit= >+cmd_ftpindex= >+readParams "$@" >+ >+# >+# Exclusive commands that will cause all others to be ignored, in order >+# of priority. >+# >+ >+if [ -n "$cmd_unlock" ]; then >+ unlock >+ return $? >+fi >+ >+if [ -n "$cmd_secure" ]; then >+ secureLock >+ return $? >+fi >+ >+if [ -n "$cmd_lock" ]; then >+ lock >+ return $? >+fi >+ >+# >+# Non-exclusive commands that do not require a lock. >+# >+ >+if [ -n "$cmd_env" ]; then >+ echo "ARCH='$ARCH'" >+ echo "BRANCH='$BRANCH'" >+ echo "FTP_TIMEOUT='$FTP_TIMEOUT'" >+ echo "PACKAGEROOT='$PACKAGEROOT'" >+ echo "PACKAGESITE='$PACKAGESITE'" >+ echo "PKG_INDEX='$PKG_INDEX'" >+ echo "PACKAGEROOT_MIRRORS='$PACKAGEROOT_MIRRORS'" >+ echo "PACKAGESITE_MIRRORS='$PACKAGESITE_MIRRORS'" >+fi >+ >+# Create a local lock if need be. >+localLock= >+if ! hasLock; then >+ localLock=1 >+ lock || return $? >+fi >+ >+# Ports tree commands. >+if [ -n "$cmd_ports" ]; then >+ if [ -n "$cmd_fetch" ]; then >+ portsnap fetch || errno="$((1 << $ERR_FETCH_PORTS | $errno))" >+ fi >+ if [ -n "$cmd_extract" ]; then >+ portsnap extract || errno=$((1 << $ERR_EXTRACT_PORTS | $errno)) >+ fi >+ if [ -n "$cmd_update" ]; then >+ portsnap update || errno=$((1 << $ERR_UPDATE_PORTS | $errno)) >+ fi >+fi >+ >+# Portaudit commands. >+if [ -n "$cmd_audit" ]; then >+ if [ -n "$cmd_fetch" ]; then >+ portaudit -F || errno=$((1 << $ERR_FETCH_VULNDB | $errno)) >+ fi >+fi >+ >+# Package index commands. >+if [ -n "$cmd_ftpindex" ]; then >+ if ! mkdir -p "${PKG_INDEX%/*}" 2> /dev/null; then >+ test -n "$verbose" \ >+ && echo "The directory ${PKG_INDEX%/*} does not exist and cannot be created!" >+ errno=$((1 << $ERR_FETCH_INDEX | $errno)) >+ elif [ -n "$cmd_fetch" ]; then >+ fetch -mo "$PKG_INDEX" "$packagetree/INDEX" \ >+ || errno=$((1 << $ERR_FETCH_INDEX | $errno)) >+ fi >+fi >+ >+ >+# Free a local lock. >+test -n "$localLock" && unlock >+ >+return $errno >+
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 193003
:
146283
|
146299
|
146301
|
146302
|
146306