#!/hint/bash # Some PKGBUILDs need CARCH to be set CARCH="x86_64" # Useful functions UMASK="" set_umask () { [ "$UMASK" == "" ] && UMASK="$(umask)" export UMASK umask 002 } restore_umask () { umask "$UMASK" >/dev/null } # just like mv -f, but we touch the file and then copy the content so # default ACLs in the target dir will be applied mv_acl() { rm -f "$2" touch "$2" cat "$1" >"$2" || return 1 rm -f "$1" } # set up general environment WORKDIR=$(mktemp -dt "${0##*/}.XXXXXXXXXX") LOCKS=() REPO_MODIFIED=0 # check if messages are to be printed using color unset ALL_OFF BOLD BLUE GREEN RED YELLOW if [[ -t 2 ]]; then ALL_OFF="$(tput sgr0)" BOLD="$(tput bold)" BLUE="${BOLD}$(tput setaf 4)" GREEN="${BOLD}$(tput setaf 2)" RED="${BOLD}$(tput setaf 1)" YELLOW="${BOLD}$(tput setaf 3)" fi readonly ALL_OFF BOLD BLUE GREEN RED YELLOW plain() { local mesg=$1; shift printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" } msg() { local mesg=$1; shift printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" } msg2() { local mesg=$1; shift printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" } warning() { local mesg=$1; shift printf "${YELLOW}==> WARNING:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } error() { local mesg=$1; shift printf "${RED}==> ERROR${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } ## # usage : in_array( $needle, $haystack ) # return : 0 - found # 1 - not found ## in_array() { local needle=$1; shift [[ -z $1 ]] && return 1 # Not Found local item for item in "$@"; do [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } ## # usage : get_full_version( $epoch, $pkgver, $pkgrel ) # return : full version spec, including epoch (if necessary), pkgver, pkgrel ## get_full_version() { if [[ $1 -eq 0 ]]; then # zero epoch case, don't include it in version echo "$2-$3" else echo "$1:$2-$3" fi } script_lock() { local LOCKDIR="$TMPDIR/.scriptlock.${0##*/}" if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then local _owner="$(/usr/bin/stat -c %U "$LOCKDIR")" error "Script %s is already locked by %s." "${0##*/}" "$_owner" exit 1 else set_umask return 0 fi } script_unlock() { local LOCKDIR="$TMPDIR/.scriptlock.${0##*/}" if [ ! -d "$LOCKDIR" ]; then warning "Script %s was not locked!" "${0##*/}" restore_umask return 1 else rmdir "$LOCKDIR" restore_umask return 0 fi } cleanup() { local l local repo local arch trap - EXIT INT QUIT TERM for l in "${LOCKS[@]}"; do repo=${l%.*} arch=${l#*.} if [ -d "$TMPDIR/.repolock.$repo.$arch" ]; then msg "Removing left over lock from [%s] (%s)" "${repo}" "${arch}" repo_unlock "$repo" "$arch" fi done if [ -d "$TMPDIR/.scriptlock.${0##*/}" ]; then msg "Removing left over lock from %s" "${0##*/}" script_unlock fi rm -rf "$WORKDIR" if (( REPO_MODIFIED )); then date +%s > "${FTP_BASE}/lastupdate" date -u +%s > "${FTP_BASE}/lastsync" fi [ "$1" ] && exit "$1" } abort() { msg 'Aborting...' cleanup 0 } die() { error "$@" cleanup 1 } trap abort INT QUIT TERM HUP trap cleanup EXIT #repo_lock [timeout] repo_lock () { local LOCKDIR="$TMPDIR/.repolock.$1.$2" local DBLOCKFILE="${FTP_BASE}/${1}/os/${2}/${1}${DBEXT}.lck" local FILESLOCKFILE="${FTP_BASE}/${1}/os/${2}/${1}${FILESEXT}.lck" local _count local _trial local _timeout local _lockblock local _owner # This is the lock file used by repo-add and repo-remove if [ -f "${DBLOCKFILE}" ]; then error "Repo [%s] (%s) is already locked by repo-{add,remove} process %s" "$1" "$2" "$(<"$DBLOCKFILE")" return 1 fi if [ -f "${FILESLOCKFILE}" ]; then error "Repo [%s] (%s) is already locked by repo-{add,remove} process %s" "$1" "$2" "$(<"$FILESLOCKFILE")" return 1 fi if [ $# -eq 2 ]; then _lockblock=true _trial=0 elif [ $# -eq 3 ]; then _lockblock=false _timeout=$3 let _trial=$_timeout/$LOCK_DELAY fi _count=0 while [ "$_count" -le "$_trial" ] || "$_lockblock" ; do if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then _owner="$(/usr/bin/stat -c %U "$LOCKDIR")" warning "Repo [%s] (%s) is already locked by %s." "${1}" "${2}" "$_owner" msg2 "Retrying in %d seconds..." "$LOCK_DELAY" else LOCKS+=("$1.$2") set_umask return 0 fi sleep "$LOCK_DELAY" let _count=$_count+1 done error "Repo [%s] (%s) is already locked by %s. Giving up!" "${1}" "${2}" "$_owner" return 1 } repo_unlock () { #repo_unlock local LOCKDIR="$TMPDIR/.repolock.$1.$2" if [ ! -d "$LOCKDIR" ]; then warning "Repo lock [%s] (%s) was not locked!" "${1}" "${2}" restore_umask return 1 else rmdir "$LOCKDIR" restore_umask return 0 fi } # usage: _grep_pkginfo pkgfile pattern _grep_pkginfo() { local _ret _ret="$(/usr/bin/bsdtar -xOqf "$1" .PKGINFO | grep -m 1 "^${2} = ")" echo "${_ret#${2} = }" } # Get the package base or name as fallback getpkgbase() { local _base _base="$(_grep_pkginfo "$1" "pkgbase")" if [ -z "$_base" ]; then getpkgname "$1" else echo "$_base" fi } issplitpkg() { local _base _base="$(_grep_pkginfo "$1" "pkgbase")" if [ -z "$_base" ]; then return 1 else return 0 fi } # Get the package name getpkgname() { local _name _name="$(_grep_pkginfo "$1" "pkgname")" if [ -z "$_name" ]; then error "Package '%s' has no pkgname in the PKGINFO. Fail!" "$1" exit 1 fi echo "$_name" } # Get the pkgver-pkgrel of this package getpkgver() { local _ver _ver="$(_grep_pkginfo "$1" "pkgver")" if [ -z "$_ver" ]; then error "Package '%s' has no pkgver in the PKGINFO. Fail!" "$1" exit 1 fi echo "$_ver" } getpkgarch() { local _ver _ver="$(_grep_pkginfo "$1" "arch")" if [ -z "$_ver" ]; then error "Package '%s' has no arch in the PKGINFO. Fail!" "$1" exit 1 fi echo "$_ver" } getpkgfile() { if [[ ${#} -ne 1 ]]; then error 'No canonical package found!' exit 1 elif [ ! -f "${1}" ]; then error "Package %s not found!" "${1}" exit 1 elif "${REQUIRE_SIGNATURE}" && [ ! -f "${1}.sig" ]; then error "Package signature %s not found!" "${1}.sig" exit 1 fi echo "${1}" } getpkgfiles() { local f if [ ! -z "$(printf '%s\n' "${@%\.*}" | sort | uniq -D)" ]; then error 'Duplicate packages found!' exit 1 fi for f in "${@}"; do if [ ! -f "${f}" ]; then error "Package %s not found!" "${f}" exit 1 elif "${REQUIRE_SIGNATURE}" && [ ! -f "${f}.sig" ]; then error "Package signature %s not found!" "${f}.sig" exit 1 fi done echo "${@}" } check_pkgfile() { local pkgfile=$1 local pkgname="$(getpkgname "${pkgfile}")" [ $? -ge 1 ] && return 1 local pkgver="$(getpkgver "${pkgfile}")" [ $? -ge 1 ] && return 1 local pkgarch="$(getpkgarch "${pkgfile}")" [ $? -ge 1 ] && return 1 in_array "${pkgarch}" "${ARCHES[@]}" 'any' || return 1 if echo "${pkgfile##*/}" | grep -q "${pkgname}-${pkgver}-${pkgarch}"; then return 0 else return 1 fi } check_pkgxbs() { local pkgfile="${1}" local _pkgbase="$(getpkgbase "${pkgfile}")" [ $? -ge 1 ] && return 1 local _pkgname="$(getpkgname "${pkgfile}")" [ $? -ge 1 ] && return 1 local _pkgver="$(getpkgver "${pkgfile}")" [ $? -ge 1 ] && return 1 local _pkgarch="$(getpkgarch "${pkgfile}")" [ $? -ge 1 ] && return 1 local repo="${2}" in_array "${repo}" "${PKGREPOS[@]}" || return 1 local xbsver="$(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; get_full_version "${_pkgname}")" [ "${xbsver}" == "${_pkgver}" ] || return 1 local xbsnames=($(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; echo "${pkgname[@]}")) in_array "${_pkgname}" "${xbsnames[@]}" || return 1 return 0 } check_splitpkgs() { local repo="${1}" shift local pkgfiles=("${@}") local pkgfile local pkgdir local xbsname mkdir -p "${WORKDIR}/check_splitpkgs/" pushd "${WORKDIR}/check_splitpkgs" >/dev/null for pkgfile in "${pkgfiles[@]}"; do issplitpkg "${pkgfile}" || continue local _pkgbase="$(getpkgbase "${pkgfile}")" msg2 "Checking %s" "$_pkgbase" local _pkgname="$(getpkgname "${pkgfile}")" local _pkgarch="$(getpkgarch "${pkgfile}")" mkdir -p "${repo}/${_pkgarch}/${_pkgbase}" echo "${_pkgname}" >> "${repo}/${_pkgarch}/${_pkgbase}/staging" local xbsnames=($(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; echo "${pkgname[@]}")) printf '%s\n' "${xbsnames[@]}" >> "${repo}/${_pkgarch}/${_pkgbase}/xbs" done popd >/dev/null for pkgdir in "${WORKDIR}/check_splitpkgs/${repo}"/*/*; do [ ! -d "${pkgdir}" ] && continue sort -u "${pkgdir}/staging" -o "${pkgdir}/staging" sort -u "${pkgdir}/xbs" -o "${pkgdir}/xbs" if [ ! -z "$(comm -13 "${pkgdir}/staging" "${pkgdir}/xbs")" ]; then return 1 fi done return 0 } check_pkgrepos() { local pkgfile=$1 local pkgname="$(getpkgname "${pkgfile}")" [ $? -ge 1 ] && return 1 local pkgver="$(getpkgver "${pkgfile}")" [ $? -ge 1 ] && return 1 local pkgarch="$(getpkgarch "${pkgfile}")" [ $? -ge 1 ] && return 1 [ -f "${FTP_BASE}/${PKGPOOL}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT} ] && return 1 [ -f "${FTP_BASE}/${PKGPOOL}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT}.sig ] && return 1 [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}" ] && return 1 [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}.sig" ] && return 1 local repo local arch for repo in "${PKGREPOS[@]}"; do for arch in "${ARCHES[@]}"; do [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT} ] && return 1 [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT}.sig ] && return 1 [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgfile##*/}" ] && return 1 [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgfile##*/}.sig" ] && return 1 done done return 0 } #usage: chk_license ${license[@]}" chk_license() { local l for l in "${@}"; do in_array "${l}" "${ALLOWED_LICENSES[@]}" && return 0 done return 1 } check_repo_permission() { local repo=$1 [ ${#PKGREPOS[@]} -eq 0 ] && return 1 [ -z "${PKGPOOL}" ] && return 1 in_array "${repo}" "${PKGREPOS[@]}" || return 1 [ -w "$FTP_BASE/${PKGPOOL}" ] || return 1 local arch for arch in "${ARCHES[@]}"; do local dir="${FTP_BASE}/${repo}/os/${arch}/" [ -w "${dir}" ] || return 1 [ -f "${dir}${repo}"${DBEXT} -a ! -w "${dir}${repo}"${DBEXT} ] && return 1 [ -f "${dir}${repo}"${FILESEXT} -a ! -w "${dir}${repo}"${FILESEXT} ] && return 1 done return 0 } set_repo_permission() { local repo=$1 local arch=$2 local dbfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" local filesfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${FILESEXT}" if [ -w "${dbfile}" ]; then local group=$(/usr/bin/stat --printf='%G' "$(dirname "${dbfile}")") chgrp "$group" "${dbfile}" || error "Could not change group of %s to %s" "${dbfile}" "$group" chgrp "$group" "${filesfile}" || error "Could not change group of %s to %s" "${filesfile}" "$group" chmod g+w "${dbfile}" || error "Could not set write permission for group %s to %s" "$group" "${dbfile}" chmod g+w "${filesfile}" || error "Could not set write permission for group %s to %s" "$group" "${filesfile}" else error "You don't have permission to change %s" "${dbfile}" fi } arch_repo_add() { local repo=$1 local arch=$2 local pkgs=("${@:3}") printf -v pkgs_str -- '%q ' "${pkgs[@]}" # package files might be relative to repo dir pushd "${FTP_BASE}/${repo}/os/${arch}" >/dev/null /usr/bin/repo-add -q "${repo}${DBEXT}" "${pkgs[@]}" >/dev/null \ || error 'repo-add %q %s' "${repo}${DBEXT}" "${pkgs_str% }" /usr/bin/repo-add -f -q "${repo}${FILESEXT}" "${pkgs[@]}" \ || error 'repo-add -f %q %s' "${repo}${FILESEXT}" "${pkgs_str% }" popd >/dev/null set_repo_permission "${repo}" "${arch}" REPO_MODIFIED=1 } arch_repo_remove() { local repo=$1 local arch=$2 local pkgs=("${@:3}") local dbfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" local filesfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${FILESEXT}" if [ ! -f "${dbfile}" ]; then error "No database found at '%s'" "${dbfile}" return 1 fi printf -v pkgs_str -- '%q ' "${pkgs[@]}" /usr/bin/repo-remove -q "${dbfile}" "${pkgs[@]}" >/dev/null \ || error 'repo-remove %q %s' "${dbfile}" "${pkgs_str% }" /usr/bin/repo-remove -q "${filesfile}" "${pkgs[@]}" \ || error 'repo-remove %q %s' "${filesfile}" "${pkgs_str% }" set_repo_permission "${repo}" "${arch}" REPO_MODIFIED=1 }