3 # repo-add - add a package to a given repo database file
4 # repo-remove - remove a package entry from a given repo database file
7 # Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # gettext initialization
25 export TEXTDOMAIN
='pacman-scripts'
26 export TEXTDOMAINDIR
='@localedir@'
28 declare -r myver
='@PACKAGE_VERSION@'
29 declare -r confdir
='@sysconfdir@'
40 # ensure we have a sane umask set
43 m4_include
(library
/output_format.sh
)
45 # print usage instructions
48 printf -- "%s (pacman) %s\n\n" "$cmd" "$myver"
49 if [[ $cmd == "repo-add" ]] ; then
50 printf -- "$(gettext "Usage
: repo-add
[options
] <path-to-db
> <package|delta
> ...
\n")"
52 printf -- "$(gettext "\
53 repo-add will update a package database by reading a package
file.
\n\
54 Multiple packages to add can be specified on the
command line.
\n")"
56 printf -- "$(gettext "Options
:\n")"
57 printf -- "$(gettext " -d, --delta generate and add delta
for package update
\n")"
58 printf -- "$(gettext " -f, --files update database
's file list\n")"
59 elif [[ $cmd == "repo-remove" ]] ; then
60 printf -- "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n")"
62 printf -- "$(gettext "\
63 repo-remove will update a package database by removing the package name\n\
64 specified on the command line from the given repo database. Multiple\n\
65 packages to remove can be specified on the command line.\n")"
67 printf -- "$(gettext "Options:\n")"
69 printf -- "$(gettext "Please move along, there is nothing to see here.\n")"
72 printf -- "$(gettext " -q, --quiet minimize output\n")"
73 printf -- "$(gettext " -s, --sign sign database with GnuPG after update\n")"
74 printf -- "$(gettext " -k, --key <key> use the specified key to sign the database\n")"
75 printf -- "$(gettext " -v, --verify verify database's signature before update
\n")"
76 printf -- "$(gettext "\n\
77 See
%s
(8) for more details and descriptions of the available options.
\n")" $cmd
79 if [[ $cmd == "repo-add" ]] ; then
80 printf -- "$(gettext "Example
: repo-add
/path
/to
/repo.db.
tar.gz pacman-3.0
.0-1-i686.pkg.
tar.gz
\n")"
81 elif [[ $cmd == "repo-remove" ]] ; then
82 printf -- "$(gettext "Example
: repo-remove
/path
/to
/repo.db.
tar.gz kernel26
\n")"
88 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
89 printf -- "$(gettext "\
90 Copyright
(c
) 2006-2012 Pacman Development Team
<pacman-dev@archlinux.org
>\n\n\
91 This is free software
; see the
source for copying conditions.
\n\
92 There is NO WARRANTY
, to the extent permitted by law.
\n")"
95 # format a metadata entry
102 printf '%%%s%%\n' "$field"
111 for pkgentry
in "$tmpdir/tree/$pkgname"*; do
113 if [[ ${name%-*-*} = $pkgname ]]; then
121 # Get the package name from the delta filename
122 get_delta_pkgname
() {
129 # write a delta entry
130 # arg1 - path to delta file
133 pkgname
=$
(get_delta_pkgname
"$deltafile")
135 pkgentry
=$
(find_pkgentry
"$pkgname")
136 if [[ -z $pkgentry ]]; then
137 error
"$(gettext "No database entry
for package
'%s'.
")" "$pkgname"
140 deltas
=$pkgentry/deltas
141 if [[ ! -f $deltas ]]; then
142 echo -e "%DELTAS%" >"$deltas"
144 # get md5sum and compressed size of package
145 md5sum=$
(openssl dgst
-md5 "$deltafile")
147 csize
=$
(@SIZECMD@
-L "$deltafile")
149 oldfile
=$
(xdelta3 printhdr
"$deltafile" |
grep "XDELTA filename (source)" |
sed 's/.*: *//')
150 newfile
=$
(xdelta3 printhdr
"$deltafile" |
grep "XDELTA filename (output)" |
sed 's/.*: *//')
152 if grep -q "$oldfile.*$newfile" "$deltas"; then
153 sed -i.backup
"/$oldfile.*$newfile/d" "$deltas" && rm -f "$deltas.backup"
155 msg2
"$(gettext "Adding
'deltas' entry
: %s
-> %s
")" "$oldfile" "$newfile"
156 echo "${deltafile##*/} $md5sum $csize $oldfile $newfile" >> "$deltas"
159 } # end db_write_delta
161 # remove a delta entry
162 # arg1 - path to delta file
165 filename
=${deltafile##*/}
166 pkgname
=$
(get_delta_pkgname
"$deltafile")
168 pkgentry
=$
(find_pkgentry
"$pkgname")
169 if [[ -z $pkgentry ]]; then
172 deltas
=$pkgentry/deltas
173 if [[ ! -f $deltas ]]; then
176 if grep -q "$filename" "$deltas"; then
177 sed -i.backup
"/$filename/d" "$deltas" && rm -f "$deltas.backup"
178 msg2
"$(gettext "Removing existing entry
'%s'...
")" "$filename"
179 # empty deltas file contains only "%DELTAS%"
180 if (( $
(wc -l < "$deltas") == 1 )); then
181 msg2
"$(gettext "Removing empty deltas
file ...
")"
188 } # end db_remove_delta
191 if ! type -p gpg
>/dev
/null
; then
192 error
"$(gettext "Cannot
find the gpg binary
! Is GnuPG installed?
")"
193 exit 1 # $E_MISSING_PROGRAM
197 # sign the package database once repackaged
199 (( ! SIGN
)) && return
202 msg
"$(gettext "Signing database...
")"
205 if [[ -n $GPGKEY ]]; then
206 SIGNWITHKEY
="-u ${GPGKEY}"
208 gpg
--detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev
/null || ret
=$?
211 msg2
"$(gettext "Created signature
file %s.
")" "${dbfile##*/.tmp.}.sig"
213 warning
"$(gettext "Failed to sign package database.
")"
217 # verify the existing package database signature
219 (( ! VERIFY
)) && return
222 msg
"$(gettext "Verifying database signature...
")"
224 if [[ ! -f $dbfile.sig
]]; then
225 warning
"$(gettext "No existing signature found
, skipping verification.
")"
228 gpg
--verify "$dbfile.sig" || ret
=$?
230 msg2
"$(gettext "Database signature
file verified.
")"
232 error
"$(gettext "Database signature was NOT valid
!")"
237 verify_repo_extension
() {
241 *.@
(db|files
).
tar.gz
) TAR_OPT
="z" ;;
242 *.@
(db|files
).
tar.bz2
) TAR_OPT
="j" ;;
243 *.@
(db|files
).
tar.xz
) TAR_OPT
="J" ;;
244 *.@
(db|files
).
tar.Z
) TAR_OPT
="Z" ;;
245 *.@
(db|files
).
tar) TAR_OPT
="" ;;
246 *) error
"$(gettext "'%s' does not have a valid archive extension.
")" \
251 printf '%s' "$TAR_OPT"
254 # write an entry to the pacman database
255 # arg1 - path to package
257 # blank out all variables
259 local -a _groups _licenses _replaces _depends _conflicts _provides \
260 _optdepends _makedepends _checkdepends
261 local pkgname pkgver pkgdesc csize size url arch builddate packager \
262 md5sum sha256sum pgpsig pgpsigsize
264 # read info from the zipped package
266 while read -r line
; do
267 [[ ${line:0:1} = '#' ]] && continue
268 IFS
=' =' read -r var val
< <(printf '%s\n' "$line")
270 # normalize whitespace with an extglob
271 declare "$var=${val//+([[:space:]])/ }"
273 group
) _groups
+=("$group") ;;
274 license
) _licenses
+=("$license") ;;
275 replaces
) _replaces
+=("$replaces") ;;
276 depend
) _depends
+=("$depend") ;;
277 conflict
) _conflicts
+=("$conflict") ;;
278 provides
) _provides
+=("$provides") ;;
279 optdepend
) _optdepends
+=("$optdepend") ;;
280 makedepend
) _makedepends
+=("$makedepend") ;;
281 checkdepend
) _checkdepends
+=("$checkdepend") ;;
283 done< <(bsdtar
-xOqf "$pkgfile" .PKGINFO
)
285 # ensure $pkgname and $pkgver variables were found
286 if [[ -z $pkgname ||
-z $pkgver ]]; then
287 error
"$(gettext "Invalid package
file '%s'.
")" "$pkgfile"
291 if [[ -d $tmpdir/tree
/$pkgname-$pkgver ]]; then
292 warning
"$(gettext "An entry
for '%s' already existed
")" "$pkgname-$pkgver"
295 pkgentry
=$
(find_pkgentry
"$pkgname")
296 if [[ -n $pkgentry ]]; then
297 local oldfilename
=$
(grep -A1 FILENAME
"$pkgentry/desc" |
tail -n1)
298 local oldfile
="$(dirname "$1")/$oldfilename"
303 # compute base64'd PGP signature
304 if [[ -f "$pkgfile.sig" ]]; then
305 pgpsigsize
=$
(@SIZECMD@
-L "$pkgfile.sig")
306 if (( pgpsigsize
> 16384 )); then
307 error
"$(gettext "Invalid package signature
file '%s'.
")" "$pkgfile.sig"
310 msg2
"$(gettext "Adding package signature...
")"
311 pgpsig
=$
(openssl base64
-in "$pkgfile.sig" |
tr -d '\n')
314 csize
=$
(@SIZECMD@
-L "$pkgfile")
317 msg2
"$(gettext "Computing checksums...
")"
318 md5sum=$
(openssl dgst
-md5 "$pkgfile")
320 sha256sum
=$
(openssl dgst
-sha256 "$pkgfile")
321 sha256sum
=${sha256sum##* }
323 # remove an existing entry if it exists, ignore failures
324 db_remove_entry
"$pkgname"
326 # create package directory
327 pushd "$tmpdir/tree" >/dev
/null
328 mkdir
"$pkgname-$pkgver"
329 pushd "$pkgname-$pkgver" >/dev
/null
331 # restore an eventual deltas file
332 [[ -f ..
/$pkgname.deltas
]] && mv "../$pkgname.deltas" deltas
335 msg2
"$(gettext "Creating
'%s' db entry...
")" 'desc'
337 format_entry
"FILENAME" "${1##*/}"
338 format_entry
"NAME" "$pkgname"
339 format_entry
"BASE" "$pkgbase"
340 format_entry
"VERSION" "$pkgver"
341 format_entry
"DESC" "$pkgdesc"
342 format_entry
"GROUPS" "${_groups[@]}"
343 format_entry
"CSIZE" "$csize"
344 format_entry
"ISIZE" "$size"
347 format_entry
"MD5SUM" "$md5sum"
348 format_entry
"SHA256SUM" "$sha256sum"
351 format_entry
"PGPSIG" "$pgpsig"
353 format_entry
"URL" "$url"
354 format_entry
"LICENSE" "${_licenses[@]}"
355 format_entry
"ARCH" "$arch"
356 format_entry
"BUILDDATE" "$builddate"
357 format_entry
"PACKAGER" "$packager"
358 format_entry
"REPLACES" "${_replaces[@]}"
361 # create depends entry
362 msg2
"$(gettext "Creating
'%s' db entry...
")" 'depends'
364 format_entry
"DEPENDS" "${_depends[@]}"
365 format_entry
"CONFLICTS" "${_conflicts[@]}"
366 format_entry
"PROVIDES" "${_provides[@]}"
367 format_entry
"OPTDEPENDS" "${_optdepends[@]}"
368 format_entry
"MAKEDEPENDS" "${_makedepends[@]}"
369 format_entry
"CHECKDEPENDS" "${_checkdepends[@]}"
375 # create files file if wanted
376 if (( WITHFILES
)); then
377 msg2
"$(gettext "Creating
'%s' db entry...
")" 'files'
378 local files_path
="$tmpdir/tree/$pkgname-$pkgver/files"
379 echo "%FILES%" >"$files_path"
380 bsdtar
--exclude='^.*' -tf "$pkgfile" >>"$files_path"
383 # create a delta file
385 if [[ -n $oldfilename ]]; then
386 if [[ -f $oldfile ]]; then
387 delta
=$
(pkgdelta
-q "$oldfile" "$1")
388 if [[ -f $delta ]]; then
389 db_write_delta
"$delta"
392 warning
"$(gettext "Old package
file not found
: %s
")" "$oldfilename"
398 } # end db_write_entry
400 # remove existing entries from the DB
401 # arg1 - package name
405 local pkgentry
=$
(find_pkgentry
"$pkgname")
406 while [[ -n $pkgentry ]]; do
408 if [[ -f $pkgentry/deltas
]]; then
409 mv "$pkgentry/deltas" "$tmpdir/tree/$pkgname.deltas"
411 msg2
"$(gettext "Removing existing entry
'%s'...
")" \
414 pkgentry
=$
(find_pkgentry
"$pkgname")
417 } # end db_remove_entry
420 case $
(( RANDOM
% 2 )) in
421 0) printf '%s\n' "H4sIAL3qBE4CAyWLwQ3AMAgD/0xh5UPzYiFUMgjq7LUJsk7yIQNAQTAikFUDnqkr" \
422 "OQFOUm0Wd9pHCi13ONjBpVdqcWx+EdXVX4vXvGv5cgztB9+fJxZ7AAAA"
425 1) printf '%s\n' "H4sIAJVWBU4CA21RMQ7DIBDbeYWrDgQJ7rZ+IA/IB05l69alcx5fc0ASVXUk4jOO" \
426 "7yAAUWtorygwJ4hlMii0YkJKKRKGvsMsiykl1SalvrMD1gUXyXRkGZPx5OPft81K" \
427 "tNAiAjyGjYO47h1JjizPkJrCWbK/4C+uLkT7bzpGc7CT9bmOzNSW5WLSO5vexjmH" \
428 "ZL9JFFZeAa0a2+lKjL2anpYfV+0Zx9LJ+/MC8nRayuDlSNy2rfAPibOzsiWHL0jL" \
431 esac | openssl base64
-d |
gzip -d
437 # ensure the path to the DB exists; $LOCKFILE is always an absolute path
438 repodir
=${LOCKFILE%/*}/
440 if [[ ! -d $repodir ]]; then
441 error
"$(gettext "%s does not exist or is not a directory.
")" "$repodir"
446 if ( set -o noclobber
; echo "$$" > "$LOCKFILE") 2> /dev
/null
; then
449 error
"$(gettext "Failed to acquire lockfile
: %s.
")" "$LOCKFILE"
450 [[ -f $LOCKFILE ]] && error
"$(gettext "Held by process
%s
")" "$(cat "$LOCKFILE")"
454 if [[ -f $REPO_DB_FILE ]]; then
455 # there are two situations we can have here- a DB with some entries,
456 # or a DB with no contents at all.
457 if ! bsdtar
-tqf "$REPO_DB_FILE" '*/desc' >/dev
/null
2>&1; then
459 if [[ -n $
(bsdtar
-tqf "$REPO_DB_FILE" '*' 2>/dev
/null
) ]]; then
460 error
"$(gettext "Repository
file '%s' is not a proper pacman database.
")" "$REPO_DB_FILE"
464 verify_signature
"$REPO_DB_FILE"
465 msg
"$(gettext "Extracting database to a temporary location...
")"
466 bsdtar
-xf "$REPO_DB_FILE" -C "$tmpdir/tree"
470 error
"$(gettext "Repository
file '%s' was not found.
")" "$REPO_DB_FILE"
474 # check if the file can be created (write permission, directory existence, etc)
475 if ! touch "$REPO_DB_FILE"; then
476 error
"$(gettext "Repository
file '%s' could not be created.
")" "$REPO_DB_FILE"
479 rm -f "$REPO_DB_FILE"
486 if [[ ! -f $1 ]]; then
487 error
"$(gettext "File
'%s' not found.
")" "$1"
491 if [[ ${1##*.} == "delta" ]]; then
493 msg
"$(gettext "Adding delta
'%s'")" "$deltafile"
494 if ! type xdelta3
&>/dev
/null
; then
495 error
"$(gettext "Cannot
find the xdelta3 binary
! Is xdelta3 installed?
")"
498 if db_write_delta
"$deltafile"; then
506 if ! bsdtar
-tqf "$pkgfile" .PKGINFO
>/dev
/null
2>&1; then
507 error
"$(gettext "'%s' is not a package
file, skipping
")" "$pkgfile"
511 msg
"$(gettext "Adding package
'%s'")" "$pkgfile"
513 db_write_entry
"$pkgfile"
517 if [[ ${1##*.} == "delta" ]]; then
519 msg
"$(gettext "Searching
for delta
'%s'...
")" "$deltafile"
520 if db_remove_delta
"$deltafile"; then
523 error
"$(gettext "Delta matching
'%s' not found.
")" "$deltafile"
529 msg
"$(gettext "Searching
for package
'%s'...
")" "$pkgname"
531 if db_remove_entry
"$pkgname"; then
532 rm -f "$tmpdir/tree/$pkgname.deltas"
535 error
"$(gettext "Package matching
'%s' not found.
")" "$pkgname"
541 # unhook all traps to avoid race conditions
542 trap '' EXIT TERM HUP QUIT INT ERR
550 local exit_code
=${1:-$?}
552 # unhook all traps to avoid race conditions
553 trap '' EXIT TERM HUP QUIT INT ERR
555 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
556 (( CLEAN_LOCK
)) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
563 # determine whether we have gettext; make it a no-op if we do not
564 if ! type gettext &>/dev
/null
; then
571 -h|
--help) usage
; exit 0;;
572 -V|
--version) version
; exit 0;;
575 # figure out what program we are
577 if [[ $cmd == "repo-elephant" ]]; then
582 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
583 error
"$(gettext "Invalid
command name
'%s' specified.
")" "$cmd"
587 tmpdir
=$
(mktemp
-d "${TMPDIR:-/tmp}/repo-tools.XXXXXXXXXX") ||
(\
588 error
"$(gettext "Cannot create temp directory
for database building.
")"; \
593 for signal
in TERM HUP QUIT
; do
594 trap "trap_exit \"$(gettext "%s signal caught. Exiting...
")\" \"$signal\"" "$signal"
596 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
597 trap 'trap_exit "$(gettext "An unknown error has occurred. Exiting...")"' ERR
604 -q|
--quiet) QUIET
=1;;
605 -d|
--delta) DELTA
=1;;
606 -f|
--files) WITHFILES
=1;;
610 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
611 if [[ ! -z $GPGKEY ]]; then
612 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
614 error
"$(gettext "There is no key
in your keyring.
")"
623 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
624 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
639 REPO_DB_FILE
=${args[0]}
640 if [[ -z $REPO_DB_FILE ]]; then
645 if [[ $REPO_DB_FILE == /* ]]; then
646 LOCKFILE
=$REPO_DB_FILE.lck
648 LOCKFILE
=$PWD/$REPO_DB_FILE.lck
651 verify_repo_extension
"$REPO_DB_FILE" >/dev
/null
654 for arg
in "${args[@]:1}"; do
656 repo-add
) add
"$arg" ;;
657 repo-remove
) remove
"$arg" ;;
661 # if at least one operation was a success, re-zip database
662 if (( success
)); then
663 msg
"$(gettext "Creating updated database
file '%s'")" "$REPO_DB_FILE"
665 TAR_OPT
=$
(verify_repo_extension
"$REPO_DB_FILE")
666 # $LOCKFILE is already guaranteed to be absolute so this is safe
667 dirname=${LOCKFILE%/*}
668 filename
=${REPO_DB_FILE##*/}
669 # this ensures we create it on the same filesystem, making moves atomic
670 tempname
=$dirname/.tmp.
$filename
672 pushd "$tmpdir/tree" >/dev
/null
673 if ( shopt -s nullglob
; files
=(*); (( ${#files[*]} )) ); then
674 bsdtar
-c${TAR_OPT}f
"$tempname" *
676 # we have no packages remaining? zip up some emptyness
677 warning
"$(gettext "No packages remain
, creating empty database.
")"
678 bsdtar
-c${TAR_OPT}f
"$tempname" -T /dev
/null
682 create_signature
"$tempname"
684 # hardlink or move the previous version of the database and signature to .old
685 # extension as a backup measure
686 if [[ -f $REPO_DB_FILE ]]; then
687 ln -f "$REPO_DB_FILE" "$REPO_DB_FILE.old" 2>/dev
/null || \
688 mv -f "$REPO_DB_FILE" "$REPO_DB_FILE.old"
690 if [[ -f $REPO_DB_FILE.sig
]]; then
691 ln -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" 2>/dev
/null || \
692 mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig"
694 rm -f "$REPO_DB_FILE.old.sig"
697 # rotate the newly-created database and signature into place
698 mv "$tempname" "$REPO_DB_FILE"
699 if [[ -f $tempname.sig
]]; then
700 mv "$tempname.sig" "$REPO_DB_FILE.sig"
703 dblink
=${REPO_DB_FILE%.tar*}
704 rm -f "$dblink" "$dblink.sig"
705 ln -s "$filename" "$dblink" 2>/dev
/null || \
706 ln "$filename" "$dblink" 2>/dev
/null || \
707 cp "$REPO_DB_FILE" "$dblink"
708 if [[ -f "$REPO_DB_FILE.sig" ]]; then
709 ln -s "$filename.sig" "$dblink.sig" 2>/dev
/null || \
710 ln "$filename.sig" "$dblink.sig" 2>/dev
/null || \
711 cp "$REPO_DB_FILE.sig" "$dblink.sig"
714 msg
"$(gettext "No packages modified
, nothing to
do.
")"
719 # vim: set ts=2 sw=2 noet: