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")"
146 md5sum="${md5sum##* }"
147 csize
=$
(@SIZECMD@
"$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"
183 } # end db_remove_delta
186 if ! type -p gpg
>/dev
/null
; then
187 error
"$(gettext "Cannot
find the gpg binary
! Is GnuPG installed?
")"
188 exit 1 # $E_MISSING_PROGRAM
192 # sign the package database once repackaged
194 (( ! SIGN
)) && return
197 msg
"$(gettext "Signing database...
")"
200 if [[ -n $GPGKEY ]]; then
201 SIGNWITHKEY
="-u ${GPGKEY}"
203 gpg
--detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev
/null || ret
=$?
206 msg2
"$(gettext "Created signature
file %s.
")" "${dbfile##*/.tmp.}.sig"
208 warning
"$(gettext "Failed to sign package database.
")"
212 # verify the existing package database signature
214 (( ! VERIFY
)) && return
217 msg
"$(gettext "Verifying database signature...
")"
219 if [[ ! -f $dbfile.sig
]]; then
220 warning
"$(gettext "No existing signature found
, skipping verification.
")"
223 gpg
--verify "$dbfile.sig" || ret
=$?
225 msg2
"$(gettext "Database signature
file verified.
")"
227 error
"$(gettext "Database signature was NOT valid
!")"
232 verify_repo_extension
() {
236 *.@
(db|files
).
tar.gz
) TAR_OPT
="z" ;;
237 *.@
(db|files
).
tar.bz2
) TAR_OPT
="j" ;;
238 *.@
(db|files
).
tar.xz
) TAR_OPT
="J" ;;
239 *.@
(db|files
).
tar.Z
) TAR_OPT
="Z" ;;
240 *.@
(db|files
).
tar) TAR_OPT
="" ;;
241 *) error
"$(gettext "'%s' does not have a valid archive extension.
")" \
246 printf '%s' "$TAR_OPT"
249 # write an entry to the pacman database
250 # arg1 - path to package
252 # blank out all variables
254 local -a _groups _licenses _replaces _depends _conflicts _provides _optdepends
255 local pkgname pkgver pkgdesc csize size url arch builddate packager \
256 md5sum sha256sum pgpsig pgpsigsize
258 # read info from the zipped package
260 while read -r line
; do
261 [[ ${line:0:1} = '#' ]] && continue
262 IFS
=' =' read -r var val
< <(printf '%s\n' "$line")
264 # normalize whitespace with an extglob
265 declare "$var=${val//+([[:space:]])/ }"
267 group
) _groups
+=("$group") ;;
268 license
) _licenses
+=("$license") ;;
269 replaces
) _replaces
+=("$replaces") ;;
270 depend
) _depends
+=("$depend") ;;
271 conflict
) _conflicts
+=("$conflict") ;;
272 provides
) _provides
+=("$provides") ;;
273 optdepend
) _optdepends
+=("$optdepend") ;;
275 done< <(bsdtar
-xOqf "$pkgfile" .PKGINFO
)
277 # ensure $pkgname and $pkgver variables were found
278 if [[ -z $pkgname ||
-z $pkgver ]]; then
279 error
"$(gettext "Invalid package
file '%s'.
")" "$pkgfile"
283 if [[ -d $tmpdir/tree
/$pkgname-$pkgver ]]; then
284 warning
"$(gettext "An entry
for '%s' already existed
")" "$pkgname-$pkgver"
287 pkgentry
=$
(find_pkgentry
$pkgname)
288 if [[ -n $pkgentry ]]; then
289 local oldfilename
=$
(grep -A1 FILENAME
$pkgentry/desc |
tail -n1)
290 local oldfile
="$(dirname $1)/$oldfilename"
295 # compute base64'd PGP signature
296 if [[ -f "$pkgfile.sig" ]]; then
297 pgpsigsize
=$
(@SIZECMD@
"$pkgfile.sig")
298 if (( pgpsigsize
> 16384 )); then
299 error
"$(gettext "Invalid package signature
file '%s'.
")" "$pkgfile.sig"
302 msg2
"$(gettext "Adding package signature...
")"
303 pgpsig
=$
(openssl base64
-in "$pkgfile.sig" |
tr -d '\n')
306 csize
=$
(@SIZECMD@
"$pkgfile")
309 msg2
"$(gettext "Computing checksums...
")"
310 md5sum="$(openssl dgst -md5 "$pkgfile")"
311 md5sum="${md5sum##* }"
312 sha256sum
="$(openssl dgst -sha256 "$pkgfile")"
313 sha256sum
="${sha256sum##* }"
315 # remove an existing entry if it exists, ignore failures
316 db_remove_entry
"$pkgname"
318 # create package directory
319 pushd "$tmpdir/tree" >/dev
/null
320 mkdir
"$pkgname-$pkgver"
321 pushd "$pkgname-$pkgver" >/dev
/null
323 # restore an eventual deltas file
324 [[ -f ..
/$pkgname.deltas
]] && mv "../$pkgname.deltas" deltas
327 msg2
"$(gettext "Creating
'%s' db entry...
")" 'desc'
329 format_entry
"FILENAME" "${1##*/}"
330 format_entry
"NAME" "$pkgname"
331 format_entry
"BASE" "$pkgbase"
332 format_entry
"VERSION" "$pkgver"
333 format_entry
"DESC" "$pkgdesc"
334 format_entry
"GROUPS" "${_groups[@]}"
335 format_entry
"CSIZE" "$csize"
336 format_entry
"ISIZE" "$size"
339 format_entry
"MD5SUM" "$md5sum"
340 format_entry
"SHA256SUM" "$sha256sum"
343 format_entry
"PGPSIG" "$pgpsig"
345 format_entry
"URL" "$url"
346 format_entry
"LICENSE" "${_licenses[@]}"
347 format_entry
"ARCH" "$arch"
348 format_entry
"BUILDDATE" "$builddate"
349 format_entry
"PACKAGER" "$packager"
350 format_entry
"REPLACES" "${_replaces[@]}"
353 # create depends entry
354 msg2
"$(gettext "Creating
'%s' db entry...
")" 'depends'
356 format_entry
"DEPENDS" "${_depends[@]}"
357 format_entry
"CONFLICTS" "${_conflicts[@]}"
358 format_entry
"PROVIDES" "${_provides[@]}"
359 format_entry
"OPTDEPENDS" "${_optdepends[@]}"
365 # create files file if wanted
366 if (( WITHFILES
)); then
367 msg2
"$(gettext "Creating
'%s' db entry...
")" 'files'
368 local files_path
="$tmpdir/tree/$pkgname-$pkgver/files"
369 echo "%FILES%" >$files_path
370 bsdtar
--exclude='^.*' -tf "$pkgfile" >>$files_path
373 # create a delta file
375 if [[ -n $oldfilename ]]; then
376 if [[ -f $oldfile ]]; then
377 delta
=$
(pkgdelta
-q $oldfile $1)
378 if [[ -f $delta ]]; then
379 db_write_delta
$delta
382 warning
"$(gettext "Old package
file not found
: %s
")" "$oldfilename"
388 } # end db_write_entry
390 # remove existing entries from the DB
391 # arg1 - package name
395 local pkgentry
=$
(find_pkgentry
$pkgname)
396 while [[ -n $pkgentry ]]; do
398 if [[ -f $pkgentry/deltas
]]; then
399 mv "$pkgentry/deltas" "$tmpdir/tree/$pkgname.deltas"
401 msg2
"$(gettext "Removing existing entry
'%s'...
")" \
404 pkgentry
=$
(find_pkgentry
$pkgname)
407 } # end db_remove_entry
410 case $
(( RANDOM
% 2 )) in
411 0) printf '%s\n' "H4sIAL3qBE4CAyWLwQ3AMAgD/0xh5UPzYiFUMgjq7LUJsk7yIQNAQTAikFUDnqkr" \
412 "OQFOUm0Wd9pHCi13ONjBpVdqcWx+EdXVX4vXvGv5cgztB9+fJxZ7AAAA"
415 1) printf '%s\n' "H4sIAJVWBU4CA21RMQ7DIBDbeYWrDgQJ7rZ+IA/IB05l69alcx5fc0ASVXUk4jOO" \
416 "7yAAUWtorygwJ4hlMii0YkJKKRKGvsMsiykl1SalvrMD1gUXyXRkGZPx5OPft81K" \
417 "tNAiAjyGjYO47h1JjizPkJrCWbK/4C+uLkT7bzpGc7CT9bmOzNSW5WLSO5vexjmH" \
418 "ZL9JFFZeAa0a2+lKjL2anpYfV+0Zx9LJ+/MC8nRayuDlSNy2rfAPibOzsiWHL0jL" \
421 esac | openssl base64
-d |
gzip -d
427 # ensure the path to the DB exists; $LOCKFILE is always an absolute path
428 repodir
=${LOCKFILE%/*}/
430 if [[ ! -d "$repodir" ]]; then
431 error
"$(gettext "%s does not exist or is not a directory.
")" "$repodir"
436 if ( set -o noclobber
; echo "$$" > "$LOCKFILE") 2> /dev
/null
; then
439 error
"$(gettext "Failed to acquire lockfile
: %s.
")" "$LOCKFILE"
440 [[ -f $LOCKFILE ]] && error
"$(gettext "Held by process
%s
")" "$(cat $LOCKFILE)"
444 if [[ -f $REPO_DB_FILE ]]; then
445 # there are two situations we can have here- a DB with some entries,
446 # or a DB with no contents at all.
447 if ! bsdtar
-tqf "$REPO_DB_FILE" '*/desc' >/dev
/null
2>&1; then
449 if [[ -n $
(bsdtar
-tqf "$REPO_DB_FILE" '*' 2>/dev
/null
) ]]; then
450 error
"$(gettext "Repository
file '%s' is not a proper pacman database.
")" "$REPO_DB_FILE"
454 verify_signature
"$REPO_DB_FILE"
455 msg
"$(gettext "Extracting database to a temporary location...
")"
456 bsdtar
-xf "$REPO_DB_FILE" -C "$tmpdir/tree"
460 error
"$(gettext "Repository
file '%s' was not found.
")" "$REPO_DB_FILE"
464 # check if the file can be created (write permission, directory existence, etc)
465 if ! touch "$REPO_DB_FILE"; then
466 error
"$(gettext "Repository
file '%s' could not be created.
")" "$REPO_DB_FILE"
469 rm -f "$REPO_DB_FILE"
476 if [[ ! -f $1 ]]; then
477 error
"$(gettext "File
'%s' not found.
")" "$1"
481 if [[ ${1##*.} == "delta" ]]; then
483 msg
"$(gettext "Adding delta
'%s'")" "$deltafile"
484 if ! type xdelta3
&>/dev
/null
; then
485 error
"$(gettext "Cannot
find the xdelta3 binary
! Is xdelta3 installed?
")"
488 if db_write_delta
"$deltafile"; then
496 if ! bsdtar
-tqf "$pkgfile" .PKGINFO
>/dev
/null
2>&1; then
497 error
"$(gettext "'%s' is not a package
file, skipping
")" "$pkgfile"
501 msg
"$(gettext "Adding package
'%s'")" "$pkgfile"
503 db_write_entry
"$pkgfile"
507 if [[ ${1##*.} == "delta" ]]; then
509 msg
"$(gettext "Searching
for delta
'%s'...
")" "$deltafile"
510 if db_remove_delta
"$deltafile"; then
513 error
"$(gettext "Delta matching
'%s' not found.
")" "$deltafile"
519 msg
"$(gettext "Searching
for package
'%s'...
")" "$pkgname"
521 if db_remove_entry
"$pkgname"; then
522 rm -f "$tmpdir/tree/$pkgname.deltas"
525 error
"$(gettext "Package matching
'%s' not found.
")" "$pkgname"
531 # unhook all traps to avoid race conditions
532 trap '' EXIT TERM HUP QUIT INT ERR
540 local exit_code
=${1:-$?}
542 # unhook all traps to avoid race conditions
543 trap '' EXIT TERM HUP QUIT INT ERR
545 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
546 (( CLEAN_LOCK
)) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
553 # determine whether we have gettext; make it a no-op if we do not
554 if ! type gettext &>/dev
/null
; then
561 -h|
--help) usage
; exit 0;;
562 -V|
--version) version
; exit 0;;
565 # figure out what program we are
567 if [[ $cmd == "repo-elephant" ]]; then
572 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
573 error
"$(gettext "Invalid
command name
'%s' specified.
")" "$cmd"
577 tmpdir
=$
(mktemp
-d "${TMPDIR:-/tmp}/repo-tools.XXXXXXXXXX") ||
(\
578 error
"$(gettext "Cannot create temp directory
for database building.
")"; \
583 for signal
in TERM HUP QUIT
; do
584 trap "trap_exit \"$(gettext "%s signal caught. Exiting...
")\" \"$signal\"" "$signal"
586 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
587 trap 'trap_exit "$(gettext "An unknown error has occurred. Exiting...")"' ERR
594 -q|
--quiet) QUIET
=1;;
595 -d|
--delta) DELTA
=1;;
596 -f|
--files) WITHFILES
=1;;
600 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
601 if [[ ! -z $GPGKEY ]]; then
602 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
604 error
"$(gettext "There is no key
in your keyring.
")"
613 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
614 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
629 REPO_DB_FILE
=${args[0]}
630 if [[ -z $REPO_DB_FILE ]]; then
635 if [[ $REPO_DB_FILE == /* ]]; then
636 LOCKFILE
=$REPO_DB_FILE.lck
638 LOCKFILE
=$PWD/$REPO_DB_FILE.lck
641 verify_repo_extension
"$REPO_DB_FILE" >/dev
/null
644 for arg
in "${args[@]:1}"; do
646 repo-add
) add
"$arg" ;;
647 repo-remove
) remove
"$arg" ;;
651 # if at least one operation was a success, re-zip database
652 if (( success
)); then
653 msg
"$(gettext "Creating updated database
file '%s'")" "$REPO_DB_FILE"
655 TAR_OPT
=$
(verify_repo_extension
"$REPO_DB_FILE")
656 # $LOCKFILE is already guaranteed to be absolute so this is safe
657 dirname=${LOCKFILE%/*}
658 filename
=${REPO_DB_FILE##*/}
659 # this ensures we create it on the same filesystem, making moves atomic
660 tempname
="$dirname/.tmp.$filename"
662 pushd "$tmpdir/tree" >/dev
/null
663 if ( shopt -s nullglob
; files
=(*); (( ${#files[*]} )) ); then
664 bsdtar
-c${TAR_OPT}f
"$tempname" *
666 # we have no packages remaining? zip up some emptyness
667 warning
"$(gettext "No packages remain
, creating empty database.
")"
668 bsdtar
-c${TAR_OPT}f
"$tempname" -T /dev
/null
672 create_signature
"$tempname"
674 # hardlink or move the previous version of the database and signature to .old
675 # extension as a backup measure
676 if [[ -f $REPO_DB_FILE ]]; then
677 ln -f "$REPO_DB_FILE" "$REPO_DB_FILE.old" 2>/dev
/null || \
678 mv -f "$REPO_DB_FILE" "$REPO_DB_FILE.old"
680 if [[ -f $REPO_DB_FILE.sig
]]; then
681 ln -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" 2>/dev
/null || \
682 mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig"
684 rm -f "$REPO_DB_FILE.old.sig"
687 # rotate the newly-created database and signature into place
688 mv "$tempname" "$REPO_DB_FILE"
689 if [[ -f $tempname.sig
]]; then
690 mv "$tempname.sig" "$REPO_DB_FILE.sig"
693 dblink
="${REPO_DB_FILE%.tar*}"
694 rm -f "$dblink" "$dblink.sig"
695 ln -s "$filename" "$dblink" 2>/dev
/null || \
696 ln "$filename" "$dblink" 2>/dev
/null || \
697 cp "$REPO_DB_FILE" "$dblink"
698 if [[ -f "$REPO_DB_FILE.sig" ]]; then
699 ln -s "$filename.sig" "$dblink.sig" 2>/dev
/null || \
700 ln "$filename.sig" "$dblink.sig" 2>/dev
/null || \
701 cp "$REPO_DB_FILE.sig" "$dblink.sig"
704 msg
"$(gettext "No packages modified
, nothing to
do.
")"
709 # vim: set ts=2 sw=2 noet: