diff options
Diffstat (limited to 'scripts/repo-add.sh.in')
-rw-r--r-- | scripts/repo-add.sh.in | 441 |
1 files changed, 289 insertions, 152 deletions
diff --git a/scripts/repo-add.sh.in b/scripts/repo-add.sh.in index 02ab389c..5e085922 100644 --- a/scripts/repo-add.sh.in +++ b/scripts/repo-add.sh.in @@ -1,11 +1,10 @@ -#!@BASH_SHELL@ +#!/bin/bash # # repo-add - add a package to a given repo database file # repo-remove - remove a package entry from a given repo database file # @configure_input@ # -# Copyright (c) 2006-2008 Aaron Griffin <aaron@archlinux.org> -# Copyright (c) 2007-2008 Dan McGee <dan@archlinux.org> +# Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,8 +19,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +shopt -s extglob + # gettext initialization -export TEXTDOMAIN='pacman' +export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' myver='@PACKAGE_VERSION@' @@ -30,6 +31,8 @@ confdir='@sysconfdir@' QUIET=0 DELTA=0 WITHFILES=0 +SIGN=0 +VERIFY=0 REPO_DB_FILE= LOCKFILE= CLEAN_LOCK=0 @@ -37,78 +40,67 @@ CLEAN_LOCK=0 # ensure we have a sane umask set umask 0022 -msg() { - (( QUIET )) && return - local mesg=$1; shift - printf "==> ${mesg}\n" "$@" >&1 -} - -msg2() { - (( QUIET )) && return - local mesg=$1; shift - printf " -> ${mesg}\n" "$@" >&1 -} - -warning() { - local mesg=$1; shift - printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2 -} - -error() { - local mesg=$1; shift - printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2 -} +m4_include(library/output_format.sh) # print usage instructions usage() { - printf "repo-add, repo-remove (pacman) %s\n\n" "$myver" - printf "$(gettext "Usage: repo-add [-d] [-f] [-q] <path-to-db> <package|delta> ...\n")" - printf "$(gettext "Usage: repo-remove [-q] <path-to-db> <packagename|delta> ...\n\n")" - printf "$(gettext "\ + cmd=${0##*/} + printf "%s (pacman) %s\n\n" "$cmd" "$myver" + if [[ $cmd == "repo-add" ]] ; then + printf "$(gettext "Usage: repo-add [options] <path-to-db> <package|delta> ...\n")" + printf "$(gettext "\ repo-add will update a package database by reading a package file.\n\ Multiple packages to add can be specified on the command line.\n\n")" - printf "$(gettext "\ + printf "$(gettext "Options:\n")" + printf "$(gettext " -d, --delta generate and add delta for package update\n")" + printf "$(gettext " -f, --files update database's file list\n")" + elif [[ $cmd == "repo-remove" ]] ; then + printf "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n\n")" + printf "$(gettext "\ repo-remove will update a package database by removing the package name\n\ specified on the command line from the given repo database. Multiple\n\ packages to remove can be specified on the command line.\n\n")" - printf "$(gettext "\ -Use the -q/--quiet flag to minimize output to basic messages, warnings,\n\ -and errors.\n\n")" - printf "$(gettext "\ -Use the -d/--delta flag to automatically generate and add a delta file\n\ -between the old entry and the new one, if the old package file is found\n\ -next to the new one.\n\n")" - printf "$(gettext "\ -Use the -f/--files flag to update a database including file entries.\n\n")" - echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0.pkg.tar.gz")" - echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")" + printf "$(gettext "Options:\n")" + fi + printf "$(gettext " -q, --quiet minimize output\n")" + printf "$(gettext " -s, --sign sign database with GnuPG after update\n")" + printf "$(gettext " -k, --key <key> use the specified key to sign the database\n")" + printf "$(gettext " -v, --verify verify database's signature before update\n")" + printf "$(gettext "\n\ +See %s(8) for more details and descriptions of the available options.\n\n")" $cmd + if [[ $cmd == "repo-add" ]] ; then + echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0-1-i686.pkg.tar.gz")" + elif [[ $cmd == "repo-remove" ]] ; then + echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")" + fi } version() { - printf "repo-add, repo-remove (pacman) %s\n\n" "$myver" + cmd=${0##*/} + printf "%s (pacman) %s\n\n" "$cmd" "$myver" printf "$(gettext "\ -Copyright (C) 2006-2008 Aaron Griffin <aaron@archlinux.org>.\n\ -Copyright (c) 2007-2008 Dan McGee <dan@archlinux.org>.\n\n\ +Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>\n\n\ This is free software; see the source for copying conditions.\n\ There is NO WARRANTY, to the extent permitted by law.\n")" } -# write a list entry +# format a metadata entry # arg1 - Entry name -# arg2 - List -# arg3 - File to write to -write_list_entry() { - if [[ -n $2 ]]; then - echo "%$1%" >>$3 - echo -e $2 >>$3 +# ... - value(s) +format_entry() { + local field=$1; shift + + if [[ $1 ]]; then + printf '%%%s%%\n' "$field" + printf '%s\n' "$@" + printf '\n' fi } -find_pkgentry() -{ +find_pkgentry() { local pkgname=$1 local pkgentry - for pkgentry in $tmpdir/$pkgname*; do + for pkgentry in $tmpdir/tree/$pkgname*; do name=${pkgentry##*/} if [[ ${name%-*-*} = $pkgname ]]; then echo $pkgentry @@ -128,8 +120,7 @@ get_delta_pkgname() { # write a delta entry # arg1 - path to delta file -db_write_delta() -{ +db_write_delta() { deltafile="$1" pkgname="$(get_delta_pkgname $deltafile)" @@ -161,8 +152,7 @@ db_write_delta() # remove a delta entry # arg1 - path to delta file -db_remove_delta() -{ +db_remove_delta() { deltafile="$1" filename=${deltafile##*/} pkgname="$(get_delta_pkgname $deltafile)" @@ -184,45 +174,111 @@ db_remove_delta() return 1 } # end db_remove_delta +check_gpg() { + if ! type -p gpg >/dev/null; then + error "$(gettext "Cannot find the gpg binary! Is GnuPG installed?")" + exit 1 # $E_MISSING_PROGRAM + fi +} + +# sign the package database once repackaged +create_signature() { + (( ! SIGN )) && return + local dbfile="$1" + local ret=0 + msg "$(gettext "Signing database...")" + + local SIGNWITHKEY="" + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY="-u ${GPGKEY}" + fi + gpg --detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev/null || ret=$? + + if (( ! ret )); then + msg2 "$(gettext "Created signature file %s.")" "${dbfile##*/}.sig" + else + warning "$(gettext "Failed to sign package database.")" + fi +} + +# verify the existing package database signature +verify_signature() { + (( ! VERIFY )) && return + local dbfile="$1" + local ret=0 + msg "$(gettext "Verifying database signature...")" + + if [[ ! -f $dbfile.sig ]]; then + warning "$(gettext "No existing signature found, skipping verification.")" + return + fi + gpg --verify "$dbfile.sig" || ret=$? + if (( ! ret )); then + msg2 "$(gettext "Database signature file verified.")" + else + error "$(gettext "Database signature was NOT valid!")" + exit 1 + fi +} + +verify_repo_extension() { + local repofile=$1 + + case "$repofile" in + *.@(db|files).tar.gz) TAR_OPT="z" ;; + *.@(db|files).tar.bz2) TAR_OPT="j" ;; + *.@(db|files).tar.xz) TAR_OPT="J" ;; + *.@(db|files).tar.Z) TAR_OPT="Z" ;; + *.@(db|files).tar) TAR_OPT="" ;; + *) error "$(gettext "'%s' does not have a valid archive extension.")" \ + "$repofile" + exit 1 ;; + esac + + printf '%s' "$TAR_OPT" +} + # write an entry to the pacman database # arg1 - path to package -db_write_entry() -{ +db_write_entry() { # blank out all variables local pkgfile="$1" - local pkgname pkgver pkgdesc csize size md5sum url arch builddate packager \ - _groups _licenses _replaces _depends _conflicts _provides _optdepends - - local OLDIFS="$IFS" - # IFS (field separator) is only the newline character - IFS=" -" + local -a _groups _licenses _replaces _depends _conflicts _provides _optdepends + local pkgname pkgver pkgdesc csize size url arch builddate packager \ + md5sum sha256sum pgpsig # read info from the zipped package local line var val - for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO | - grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do - # bash awesomeness here- var is always one word, val is everything else - var=${line%% *} - val=${line#* } - declare $var="$val" + while read -r line; do + [[ ${line:0:1} = '#' ]] && continue + IFS=' =' read -r var val < <(printf '%s\n' "$line") + + # normalize whitespace with an extglob + declare "$var=${val//+([[:space:]])/ }" case "$var" in - group) _groups="$_groups$group\n" ;; - license) _licenses="$_licenses$license\n" ;; - replaces) _replaces="$_replaces$replaces\n" ;; - depend) _depends="$_depends$depend\n" ;; - conflict) _conflicts="$_conflicts$conflict\n" ;; - provides) _provides="$_provides$provides\n" ;; - optdepend) _optdepends="$_optdepends$optdepend\n" ;; + group) _groups+=("$group") ;; + license) _licenses+=("$license") ;; + replaces) _replaces+=("$replaces") ;; + depend) _depends+=("$depend") ;; + conflict) _conflicts+=("$conflict") ;; + provides) _provides+=("$provides") ;; + optdepend) _optdepends+=("$optdepend") ;; esac - done + done< <(bsdtar -xOqf "$pkgfile" .PKGINFO) - IFS=$OLDIFS + csize=$(@SIZECMD@ "$pkgfile") - # get md5sum and compressed size of package + # compute checksums + msg2 "$(gettext "Computing checksums...")" md5sum="$(openssl dgst -md5 "$pkgfile")" md5sum="${md5sum##* }" - csize=$(@SIZECMD@ "$pkgfile") + sha256sum="$(openssl dgst -sha256 "$pkgfile")" + sha256sum="${sha256sum##* }" + + # compute base64'd PGP signature + if [[ -f "$pkgfile.sig" ]]; then + pgpsig=$(openssl base64 -in "$pkgfile.sig" | tr -d '\n') + fi # ensure $pkgname and $pkgver variables were found if [[ -z $pkgname || -z $pkgver ]]; then @@ -230,7 +286,7 @@ db_write_entry() return 1 fi - pushd "$tmpdir" >/dev/null + pushd "$tmpdir/tree" >/dev/null if [[ -d $pkgname-$pkgver ]]; then warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver" else @@ -255,34 +311,39 @@ db_write_entry() # create desc entry msg2 "$(gettext "Creating '%s' db entry...")" 'desc' - echo -e "%FILENAME%\n$(basename "$1")\n" >>desc - echo -e "%NAME%\n$pkgname\n" >>desc - [[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc - echo -e "%VERSION%\n$pkgver\n" >>desc - [[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc - write_list_entry "GROUPS" "$_groups" "desc" - [[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc - [[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc - - # compute checksums - msg2 "$(gettext "Computing md5 checksums...")" - echo -e "%MD5SUM%\n$md5sum\n" >>desc - - [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc - write_list_entry "LICENSE" "$_licenses" "desc" - [[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc - [[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc - [[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc - write_list_entry "REPLACES" "$_replaces" "desc" + { + format_entry "FILENAME" "${1##*/}" + format_entry "NAME" "$pkgname" + format_entry "BASE" "$pkgbase" + format_entry "VERSION" "$pkgver" + format_entry "DESC" "$pkgdesc" + format_entry "GROUPS" "${_groups[@]}" + format_entry "CSIZE" "$csize" + format_entry "ISIZE" "$size" + + # add checksums + format_entry "MD5SUM" "$md5sum" + format_entry "SHA256SUM" "$sha256sum" + + # add PGP sig + format_entry "PGPSIG" "$pgpsig" + + format_entry "URL" "$url" + format_entry "LICENSE" "${_licenses[@]}" + format_entry "ARCH" "$arch" + format_entry "BUILDDATE" "$builddate" + format_entry "PACKAGER" "$packager" + format_entry "REPLACES" "${_replaces[@]}" + } >'desc' # create depends entry msg2 "$(gettext "Creating '%s' db entry...")" 'depends' - # create the file even if it will remain empty - touch "depends" - write_list_entry "DEPENDS" "$_depends" "depends" - write_list_entry "CONFLICTS" "$_conflicts" "depends" - write_list_entry "PROVIDES" "$_provides" "depends" - write_list_entry "OPTDEPENDS" "$_optdepends" "depends" + { + format_entry "DEPENDS" "${_depends[@]}" + format_entry "CONFLICTS" "${_conflicts[@]}" + format_entry "PROVIDES" "${_provides[@]}" + format_entry "OPTDEPENDS" "${_optdepends[@]}" + } >'depends' popd >/dev/null popd >/dev/null @@ -290,7 +351,7 @@ db_write_entry() # create files file if wanted if (( WITHFILES )); then msg2 "$(gettext "Creating '%s' db entry...")" 'files' - local files_path="$tmpdir/$pkgname-$pkgver/files" + local files_path="$tmpdir/tree/$pkgname-$pkgver/files" echo "%FILES%" >$files_path bsdtar --exclude='^.*' -tf "$pkgfile" >>$files_path fi @@ -321,18 +382,47 @@ db_remove_entry() { while [[ -n $pkgentry ]]; do notfound=0 if [[ -f $pkgentry/deltas ]]; then - mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas" + mv "$pkgentry/deltas" "$tmpdir/tree/$pkgname.deltas" fi msg2 "$(gettext "Removing existing entry '%s'...")" \ - "$(basename $pkgentry)" + "${pkgentry##*/}" rm -rf $pkgentry pkgentry=$(find_pkgentry $pkgname) done return $notfound } # end db_remove_entry -check_repo_db() -{ +elephant() { + case $(( RANDOM % 2 )) in + 0) printf '%s\n' "H4sIAL3qBE4CAyWLwQ3AMAgD/0xh5UPzYiFUMgjq7LUJsk7yIQNAQTAikFUDnqkr" \ + "OQFOUm0Wd9pHCi13ONjBpVdqcWx+EdXVX4vXvGv5cgztB9+fJxZ7AAAA" + ;; + + 1) printf '%s\n' "H4sIAJVWBU4CA21RMQ7DIBDbeYWrDgQJ7rZ+IA/IB05l69alcx5fc0ASVXUk4jOO" \ + "7yAAUWtorygwJ4hlMii0YkJKKRKGvsMsiykl1SalvrMD1gUXyXRkGZPx5OPft81K" \ + "tNAiAjyGjYO47h1JjizPkJrCWbK/4C+uLkT7bzpGc7CT9bmOzNSW5WLSO5vexjmH" \ + "ZL9JFFZeAa0a2+lKjL2anpYfV+0Zx9LJ+/MC8nRayuDlSNy2rfAPibOzsiWHL0jL" \ + "SsjFAQAA" + ;; + esac | openssl base64 -d | gzip -d +} + +check_repo_db() { + local repodir + + # ensure the path to the DB exists + if [[ "$LOCKFILE" == /* ]]; then + repodir=${LOCKFILE%/*}/ + else + repodir=$PWD/$LOCKFILE + repodir=${repodir%/*}/ + fi + + if [[ ! -d "$repodir" ]]; then + error "$(gettext "%s does not exist or is not a directory.")" "$repodir" + exit 1 + fi + # check lock file if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then CLEAN_LOCK=1 @@ -352,8 +442,9 @@ check_repo_db() exit 1 fi fi + verify_signature "$REPO_DB_FILE" msg "$(gettext "Extracting database to a temporary location...")" - bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir" + bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir/tree" else case "$cmd" in repo-remove) @@ -372,8 +463,7 @@ check_repo_db() fi } -add() -{ +add() { if [[ ! -f $1 ]]; then error "$(gettext "File '%s' not found.")" "$1" return 1 @@ -404,8 +494,7 @@ add() db_write_entry "$pkgfile" } -remove() -{ +remove() { if [[ ${1##*.} == "delta" ]]; then deltafile=$1 msg "$(gettext "Searching for delta '%s'...")" "$deltafile" @@ -421,7 +510,7 @@ remove() msg "$(gettext "Searching for package '%s'...")" "$pkgname" if db_remove_entry "$pkgname"; then - rm -f "$tmpdir/$pkgname.deltas" + rm -f "$tmpdir/tree/$pkgname.deltas" return 0 else error "$(gettext "Package matching '%s' not found.")" "$pkgname" @@ -429,8 +518,7 @@ remove() fi } -trap_exit() -{ +trap_exit() { echo error "$@" exit 1 @@ -460,7 +548,12 @@ case "$1" in esac # figure out what program we are -cmd="$(basename $0)" +cmd=${0##*/} +if [[ $cmd == "repo-elephant" ]]; then + elephant + exit 0 +fi + if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then error "$(gettext "Invalid command name '%s' specified.")" "$cmd" exit 1 @@ -469,65 +562,109 @@ fi tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\ error "$(gettext "Cannot create temp directory for database building.")"; \ exit 1) +mkdir $tmpdir/tree trap 'clean_up' EXIT trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR +declare -a args success=0 # parse arguments -for arg in "$@"; do - case "$arg" in +while (( $# )); do + case "$1" in -q|--quiet) QUIET=1;; -d|--delta) DELTA=1;; -f|--files) WITHFILES=1;; - *) - if [[ -z $REPO_DB_FILE ]]; then - REPO_DB_FILE="$arg" - LOCKFILE="$REPO_DB_FILE.lck" - check_repo_db - else - case "$cmd" in - repo-add) add $arg && success=1 ;; - repo-remove) remove $arg && success=1 ;; - esac + -s|--sign) + check_gpg + SIGN=1 + if ! gpg --list-key ${GPGKEY} &>/dev/null; then + if [[ ! -z $GPGKEY ]]; then + error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")" + else + error "$(gettext "There is no key in your keyring.")" + fi + exit 1 fi ;; + -k|--key) + check_gpg + shift + GPGKEY="$1" + if ! gpg --list-key ${GPGKEY} &>/dev/null; then + error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")" + exit 1 + fi + ;; + -v|--verify) + check_gpg + VERIFY=1 + ;; + *) + args+=("$1") + ;; esac + shift +done + +REPO_DB_FILE=${args[0]} +if [[ -z $REPO_DB_FILE ]]; then + usage + exit 1 +fi + +LOCKFILE=$REPO_DB_FILE.lck + +verify_repo_extension "$REPO_DB_FILE" >/dev/null +check_repo_db + +for arg in "${args[@]:1}"; do + case "$cmd" in + repo-add) add "$arg" ;; + repo-remove) remove "$arg" ;; + esac && success=1 done # if at least one operation was a success, re-zip database if (( success )); then msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE" - case "$REPO_DB_FILE" in - *tar.gz) TAR_OPT="z" ;; - *tar.bz2) TAR_OPT="j" ;; - *tar.xz) TAR_OPT="J" ;; - *) warning "$(gettext "'%s' does not have a valid archive extension.")" \ - "$REPO_DB_FILE" ;; - esac - - filename=$(basename "$REPO_DB_FILE") + TAR_OPT=$(verify_repo_extension "$REPO_DB_FILE") + filename=${REPO_DB_FILE##*/} - pushd "$tmpdir" >/dev/null - if [[ -n $(ls) ]]; then - bsdtar -c${TAR_OPT}f "$filename" * + pushd "$tmpdir/tree" >/dev/null + if ( shopt -s nullglob; files=(*); (( ${#files[*]} )) ); then + bsdtar -c${TAR_OPT}f "$tmpdir/$filename" * else # we have no packages remaining? zip up some emptyness warning "$(gettext "No packages remain, creating empty database.")" - bsdtar -c${TAR_OPT}f "$filename" -T /dev/null + bsdtar -c${TAR_OPT}f "$tmpdir/$filename" -T /dev/null fi popd >/dev/null + create_signature "$tmpdir/$filename" + [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old" + if [[ -f $REPO_DB_FILE.sig ]]; then + mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" + else + rm -f "$REPO_DB_FILE.old.sig" + fi [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE" - dblink="${REPO_DB_FILE%.tar.*}" + [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig" + dblink="${REPO_DB_FILE%.tar*}" target=${REPO_DB_FILE##*/} - ln -sf "$target" "$dblink" 2>/dev/null || \ - ln -f "$target" "$dblink" 2>/dev/null || \ + rm -f "$dblink" "$dblink.sig" + ln -s "$target" "$dblink" 2>/dev/null || \ + ln "$target" "$dblink" 2>/dev/null || \ cp "$REPO_DB_FILE" "$dblink" + if [[ -f "$target.sig" ]]; then + ln -s "$target.sig" "$dblink.sig" 2>/dev/null || \ + ln "$target.sig" "$dblink.sig" 2>/dev/null || \ + cp "$REPO_DB_FILE.sig" "$dblink.sig" + fi else msg "$(gettext "No packages modified, nothing to do.")" exit 1 |