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-2011 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 repo-add will update a package database by reading a package
file.
\n\
53 Multiple packages to add can be specified on the
command line.
\n\n")"
54 printf "$(gettext "Options
:\n")"
55 printf "$(gettext " -d, --delta generate and add delta
for package update
\n")"
56 printf "$(gettext " -f, --files update database
's file list\n")"
57 elif [[ $cmd == "repo-remove" ]] ; then
58 printf "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n\n")"
60 repo-remove will update a package database by removing the package name\n\
61 specified on the command line from the given repo database. Multiple\n\
62 packages to remove can be specified on the command line.\n\n")"
63 printf "$(gettext "Options:\n")"
65 printf "$(gettext " -q, --quiet minimize output\n")"
66 printf "$(gettext " -s, --sign sign database with GnuPG after update\n")"
67 printf "$(gettext " -k, --key <key> use the specified key to sign the database\n")"
68 printf "$(gettext " -v, --verify verify database's signature before update
\n")"
69 printf "$(gettext "\n\
70 See
%s
(8) for more details and descriptions of the available options.
\n\n")" $cmd
71 if [[ $cmd == "repo-add" ]] ; then
72 echo "$(gettext "Example
: repo-add
/path
/to
/repo.db.
tar.gz pacman-3.0
.0-1-i686.pkg.
tar.gz
")"
73 elif [[ $cmd == "repo-remove" ]] ; then
74 echo "$(gettext "Example
: repo-remove
/path
/to
/repo.db.
tar.gz kernel26
")"
80 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
82 Copyright
(c
) 2006-2011 Pacman Development Team
<pacman-dev@archlinux.org
>\n\n\
83 This is free software
; see the
source for copying conditions.
\n\
84 There is NO WARRANTY
, to the extent permitted by law.
\n")"
87 # format a metadata entry
94 printf '%%%s%%\n' "$field"
103 for pkgentry
in $tmpdir/tree
/$pkgname*; do
105 if [[ ${name%-*-*} = $pkgname ]]; then
113 # Get the package name from the delta filename
114 get_delta_pkgname
() {
121 # write a delta entry
122 # arg1 - path to delta file
125 pkgname
="$(get_delta_pkgname $deltafile)"
127 pkgentry
=$
(find_pkgentry
$pkgname)
128 if [[ -z $pkgentry ]]; then
129 error
"$(gettext "No database entry
for package
'%s'.
")" "$pkgname"
132 deltas
="$pkgentry/deltas"
133 if [[ ! -f $deltas ]]; then
134 echo -e "%DELTAS%" >$deltas
136 # get md5sum and compressed size of package
137 md5sum="$(openssl dgst -md5 "$deltafile")"
138 md5sum="${md5sum##* }"
139 csize
=$
(@SIZECMD@
"$deltafile")
141 oldfile
=$
(xdelta3 printhdr
$deltafile |
grep "XDELTA filename (source)" |
sed 's/.*: *//')
142 newfile
=$
(xdelta3 printhdr
$deltafile |
grep "XDELTA filename (output)" |
sed 's/.*: *//')
144 if grep -q "$oldfile.*$newfile" $deltas; then
145 sed -i.backup
"/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
147 msg2
"$(gettext "Adding
'deltas' entry
: %s
-> %s
")" "$oldfile" "$newfile"
148 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
151 } # end db_write_delta
153 # remove a delta entry
154 # arg1 - path to delta file
157 filename
=${deltafile##*/}
158 pkgname
="$(get_delta_pkgname $deltafile)"
160 pkgentry
=$
(find_pkgentry
$pkgname)
161 if [[ -z $pkgentry ]]; then
164 deltas
="$pkgentry/deltas"
165 if [[ ! -f $deltas ]]; then
168 if grep -q "$filename" $deltas; then
169 sed -i.backup
"/$filename/d" $deltas && rm -f $deltas.backup
170 msg2
"$(gettext "Removing existing entry
'%s'...
")" "$filename"
175 } # end db_remove_delta
178 if ! type -p gpg
>/dev
/null
; then
179 error
"$(gettext "Cannot
find the gpg binary
! Is GnuPG installed?
")"
180 exit 1 # $E_MISSING_PROGRAM
184 # sign the package database once repackaged
186 (( ! SIGN
)) && return
189 msg
"$(gettext "Signing database...
")"
192 if [[ -n $GPGKEY ]]; then
193 SIGNWITHKEY
="-u ${GPGKEY}"
195 gpg
--detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev
/null || ret
=$?
198 msg2
"$(gettext "Created signature
file %s.
")" "${dbfile##*/.tmp.}.sig"
200 warning
"$(gettext "Failed to sign package database.
")"
204 # verify the existing package database signature
206 (( ! VERIFY
)) && return
209 msg
"$(gettext "Verifying database signature...
")"
211 if [[ ! -f $dbfile.sig
]]; then
212 warning
"$(gettext "No existing signature found
, skipping verification.
")"
215 gpg
--verify "$dbfile.sig" || ret
=$?
217 msg2
"$(gettext "Database signature
file verified.
")"
219 error
"$(gettext "Database signature was NOT valid
!")"
224 verify_repo_extension
() {
228 *.@
(db|files
).
tar.gz
) TAR_OPT
="z" ;;
229 *.@
(db|files
).
tar.bz2
) TAR_OPT
="j" ;;
230 *.@
(db|files
).
tar.xz
) TAR_OPT
="J" ;;
231 *.@
(db|files
).
tar.Z
) TAR_OPT
="Z" ;;
232 *.@
(db|files
).
tar) TAR_OPT
="" ;;
233 *) error
"$(gettext "'%s' does not have a valid archive extension.
")" \
238 printf '%s' "$TAR_OPT"
241 # write an entry to the pacman database
242 # arg1 - path to package
244 # blank out all variables
246 local -a _groups _licenses _replaces _depends _conflicts _provides _optdepends
247 local pkgname pkgver pkgdesc csize size url arch builddate packager \
248 md5sum sha256sum pgpsig pgpsigsize
250 # read info from the zipped package
252 while read -r line
; do
253 [[ ${line:0:1} = '#' ]] && continue
254 IFS
=' =' read -r var val
< <(printf '%s\n' "$line")
256 # normalize whitespace with an extglob
257 declare "$var=${val//+([[:space:]])/ }"
259 group
) _groups
+=("$group") ;;
260 license
) _licenses
+=("$license") ;;
261 replaces
) _replaces
+=("$replaces") ;;
262 depend
) _depends
+=("$depend") ;;
263 conflict
) _conflicts
+=("$conflict") ;;
264 provides
) _provides
+=("$provides") ;;
265 optdepend
) _optdepends
+=("$optdepend") ;;
267 done< <(bsdtar
-xOqf "$pkgfile" .PKGINFO
)
269 # ensure $pkgname and $pkgver variables were found
270 if [[ -z $pkgname ||
-z $pkgver ]]; then
271 error
"$(gettext "Invalid package
file '%s'.
")" "$pkgfile"
275 if [[ -d $tmpdir/tree
/$pkgname-$pkgver ]]; then
276 warning
"$(gettext "An entry
for '%s' already existed
")" "$pkgname-$pkgver"
279 pkgentry
=$
(find_pkgentry
$pkgname)
280 if [[ -n $pkgentry ]]; then
281 local oldfilename
=$
(grep -A1 FILENAME
$pkgentry/desc |
tail -n1)
282 local oldfile
="$(dirname $1)/$oldfilename"
287 # compute base64'd PGP signature
288 if [[ -f "$pkgfile.sig" ]]; then
289 pgpsigsize
=$
(@SIZECMD@
"$pkgfile.sig")
290 if (( pgpsigsize
> 16384 )); then
291 error
"$(gettext "Invalid package signature
file '%s'.
")" "$pkgfile.sig"
294 msg2
"$(gettext "Adding package signature...
")"
295 pgpsig
=$
(openssl base64
-in "$pkgfile.sig" |
tr -d '\n')
298 csize
=$
(@SIZECMD@
"$pkgfile")
301 msg2
"$(gettext "Computing checksums...
")"
302 md5sum="$(openssl dgst -md5 "$pkgfile")"
303 md5sum="${md5sum##* }"
304 sha256sum
="$(openssl dgst -sha256 "$pkgfile")"
305 sha256sum
="${sha256sum##* }"
307 # remove an existing entry if it exists, ignore failures
308 db_remove_entry
"$pkgname"
310 # create package directory
311 pushd "$tmpdir/tree" >/dev
/null
312 mkdir
"$pkgname-$pkgver"
313 pushd "$pkgname-$pkgver" >/dev
/null
315 # restore an eventual deltas file
316 [[ -f ..
/$pkgname.deltas
]] && mv "../$pkgname.deltas" deltas
319 msg2
"$(gettext "Creating
'%s' db entry...
")" 'desc'
321 format_entry
"FILENAME" "${1##*/}"
322 format_entry
"NAME" "$pkgname"
323 format_entry
"BASE" "$pkgbase"
324 format_entry
"VERSION" "$pkgver"
325 format_entry
"DESC" "$pkgdesc"
326 format_entry
"GROUPS" "${_groups[@]}"
327 format_entry
"CSIZE" "$csize"
328 format_entry
"ISIZE" "$size"
331 format_entry
"MD5SUM" "$md5sum"
332 format_entry
"SHA256SUM" "$sha256sum"
335 format_entry
"PGPSIG" "$pgpsig"
337 format_entry
"URL" "$url"
338 format_entry
"LICENSE" "${_licenses[@]}"
339 format_entry
"ARCH" "$arch"
340 format_entry
"BUILDDATE" "$builddate"
341 format_entry
"PACKAGER" "$packager"
342 format_entry
"REPLACES" "${_replaces[@]}"
345 # create depends entry
346 msg2
"$(gettext "Creating
'%s' db entry...
")" 'depends'
348 format_entry
"DEPENDS" "${_depends[@]}"
349 format_entry
"CONFLICTS" "${_conflicts[@]}"
350 format_entry
"PROVIDES" "${_provides[@]}"
351 format_entry
"OPTDEPENDS" "${_optdepends[@]}"
357 # create files file if wanted
358 if (( WITHFILES
)); then
359 msg2
"$(gettext "Creating
'%s' db entry...
")" 'files'
360 local files_path
="$tmpdir/tree/$pkgname-$pkgver/files"
361 echo "%FILES%" >$files_path
362 bsdtar
--exclude='^.*' -tf "$pkgfile" >>$files_path
365 # create a delta file
367 if [[ -n $oldfilename ]]; then
368 if [[ -f $oldfile ]]; then
369 delta
=$
(pkgdelta
-q $oldfile $1)
370 if [[ -f $delta ]]; then
371 db_write_delta
$delta
374 warning
"$(gettext "Old package
file not found
: %s
")" "$oldfilename"
380 } # end db_write_entry
382 # remove existing entries from the DB
383 # arg1 - package name
387 local pkgentry
=$
(find_pkgentry
$pkgname)
388 while [[ -n $pkgentry ]]; do
390 if [[ -f $pkgentry/deltas
]]; then
391 mv "$pkgentry/deltas" "$tmpdir/tree/$pkgname.deltas"
393 msg2
"$(gettext "Removing existing entry
'%s'...
")" \
396 pkgentry
=$
(find_pkgentry
$pkgname)
399 } # end db_remove_entry
402 case $
(( RANDOM
% 2 )) in
403 0) printf '%s\n' "H4sIAL3qBE4CAyWLwQ3AMAgD/0xh5UPzYiFUMgjq7LUJsk7yIQNAQTAikFUDnqkr" \
404 "OQFOUm0Wd9pHCi13ONjBpVdqcWx+EdXVX4vXvGv5cgztB9+fJxZ7AAAA"
407 1) printf '%s\n' "H4sIAJVWBU4CA21RMQ7DIBDbeYWrDgQJ7rZ+IA/IB05l69alcx5fc0ASVXUk4jOO" \
408 "7yAAUWtorygwJ4hlMii0YkJKKRKGvsMsiykl1SalvrMD1gUXyXRkGZPx5OPft81K" \
409 "tNAiAjyGjYO47h1JjizPkJrCWbK/4C+uLkT7bzpGc7CT9bmOzNSW5WLSO5vexjmH" \
410 "ZL9JFFZeAa0a2+lKjL2anpYfV+0Zx9LJ+/MC8nRayuDlSNy2rfAPibOzsiWHL0jL" \
413 esac | openssl base64
-d |
gzip -d
419 # ensure the path to the DB exists; $LOCKFILE is always an absolute path
420 repodir
=${LOCKFILE%/*}/
422 if [[ ! -d "$repodir" ]]; then
423 error
"$(gettext "%s does not exist or is not a directory.
")" "$repodir"
428 if ( set -o noclobber
; echo "$$" > "$LOCKFILE") 2> /dev
/null
; then
431 error
"$(gettext "Failed to acquire lockfile
: %s.
")" "$LOCKFILE"
432 [[ -f $LOCKFILE ]] && error
"$(gettext "Held by process
%s
")" "$(cat $LOCKFILE)"
436 if [[ -f $REPO_DB_FILE ]]; then
437 # there are two situations we can have here- a DB with some entries,
438 # or a DB with no contents at all.
439 if ! bsdtar
-tqf "$REPO_DB_FILE" '*/desc' >/dev
/null
2>&1; then
441 if [[ -n $
(bsdtar
-tqf "$REPO_DB_FILE" '*' 2>/dev
/null
) ]]; then
442 error
"$(gettext "Repository
file '%s' is not a proper pacman database.
")" "$REPO_DB_FILE"
446 verify_signature
"$REPO_DB_FILE"
447 msg
"$(gettext "Extracting database to a temporary location...
")"
448 bsdtar
-xf "$REPO_DB_FILE" -C "$tmpdir/tree"
452 error
"$(gettext "Repository
file '%s' was not found.
")" "$REPO_DB_FILE"
456 # check if the file can be created (write permission, directory existence, etc)
457 if ! touch "$REPO_DB_FILE"; then
458 error
"$(gettext "Repository
file '%s' could not be created.
")" "$REPO_DB_FILE"
461 rm -f "$REPO_DB_FILE"
468 if [[ ! -f $1 ]]; then
469 error
"$(gettext "File
'%s' not found.
")" "$1"
473 if [[ ${1##*.} == "delta" ]]; then
475 msg
"$(gettext "Adding delta
'%s'")" "$deltafile"
476 if ! type xdelta3
&>/dev
/null
; then
477 error
"$(gettext "Cannot
find the xdelta3 binary
! Is xdelta3 installed?
")"
480 if db_write_delta
"$deltafile"; then
488 if ! bsdtar
-tqf "$pkgfile" .PKGINFO
>/dev
/null
2>&1; then
489 error
"$(gettext "'%s' is not a package
file, skipping
")" "$pkgfile"
493 msg
"$(gettext "Adding package
'%s'")" "$pkgfile"
495 db_write_entry
"$pkgfile"
499 if [[ ${1##*.} == "delta" ]]; then
501 msg
"$(gettext "Searching
for delta
'%s'...
")" "$deltafile"
502 if db_remove_delta
"$deltafile"; then
505 error
"$(gettext "Delta matching
'%s' not found.
")" "$deltafile"
511 msg
"$(gettext "Searching
for package
'%s'...
")" "$pkgname"
513 if db_remove_entry
"$pkgname"; then
514 rm -f "$tmpdir/tree/$pkgname.deltas"
517 error
"$(gettext "Package matching
'%s' not found.
")" "$pkgname"
523 # unhook all traps to avoid race conditions
524 trap '' EXIT TERM HUP QUIT INT ERR
532 local exit_code
=${1:-$?}
534 # unhook all traps to avoid race conditions
535 trap '' EXIT TERM HUP QUIT INT ERR
537 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
538 (( CLEAN_LOCK
)) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
545 # determine whether we have gettext; make it a no-op if we do not
546 if ! type gettext &>/dev
/null
; then
553 -h|
--help) usage
; exit 0;;
554 -V|
--version) version
; exit 0;;
557 # figure out what program we are
559 if [[ $cmd == "repo-elephant" ]]; then
564 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
565 error
"$(gettext "Invalid
command name
'%s' specified.
")" "$cmd"
569 tmpdir
=$
(mktemp
-d "${TMPDIR:-/tmp}/repo-tools.XXXXXXXXXX") ||
(\
570 error
"$(gettext "Cannot create temp directory
for database building.
")"; \
575 for signal
in TERM HUP QUIT
; do
576 trap "trap_exit \"$(gettext "%s signal caught. Exiting...
")\" \"$signal\"" "$signal"
578 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
579 trap 'trap_exit "$(gettext "An unknown error has occurred. Exiting...")"' ERR
586 -q|
--quiet) QUIET
=1;;
587 -d|
--delta) DELTA
=1;;
588 -f|
--files) WITHFILES
=1;;
592 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
593 if [[ ! -z $GPGKEY ]]; then
594 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
596 error
"$(gettext "There is no key
in your keyring.
")"
605 if ! gpg
--list-key ${GPGKEY} &>/dev
/null
; then
606 error
"$(gettext "The key
${GPGKEY} does not exist
in your keyring.
")"
621 REPO_DB_FILE
=${args[0]}
622 if [[ -z $REPO_DB_FILE ]]; then
627 if [[ $REPO_DB_FILE == /* ]]; then
628 LOCKFILE
=$REPO_DB_FILE.lck
630 LOCKFILE
=$PWD/$REPO_DB_FILE.lck
633 verify_repo_extension
"$REPO_DB_FILE" >/dev
/null
636 for arg
in "${args[@]:1}"; do
638 repo-add
) add
"$arg" ;;
639 repo-remove
) remove
"$arg" ;;
643 # if at least one operation was a success, re-zip database
644 if (( success
)); then
645 msg
"$(gettext "Creating updated database
file '%s'")" "$REPO_DB_FILE"
647 TAR_OPT
=$
(verify_repo_extension
"$REPO_DB_FILE")
648 # $LOCKFILE is already guaranteed to be absolute so this is safe
649 dirname=${LOCKFILE%/*}
650 filename
=${REPO_DB_FILE##*/}
651 # this ensures we create it on the same filesystem, making moves atomic
652 tempname
="$dirname/.tmp.$filename"
654 pushd "$tmpdir/tree" >/dev
/null
655 if ( shopt -s nullglob
; files
=(*); (( ${#files[*]} )) ); then
656 bsdtar
-c${TAR_OPT}f
"$tempname" *
658 # we have no packages remaining? zip up some emptyness
659 warning
"$(gettext "No packages remain
, creating empty database.
")"
660 bsdtar
-c${TAR_OPT}f
"$tempname" -T /dev
/null
664 create_signature
"$tempname"
666 # hardlink or move the previous version of the database and signature to .old
667 # extension as a backup measure
668 if [[ -f $REPO_DB_FILE ]]; then
669 ln -f "$REPO_DB_FILE" "$REPO_DB_FILE.old" 2>/dev
/null || \
670 mv -f "$REPO_DB_FILE" "$REPO_DB_FILE.old"
672 if [[ -f $REPO_DB_FILE.sig
]]; then
673 ln -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" 2>/dev
/null || \
674 mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig"
676 rm -f "$REPO_DB_FILE.old.sig"
679 # rotate the newly-created database and signature into place
680 mv "$tempname" "$REPO_DB_FILE"
681 if [[ -f $tempname.sig
]]; then
682 mv "$tempname.sig" "$REPO_DB_FILE.sig"
685 dblink
="${REPO_DB_FILE%.tar*}"
686 rm -f "$dblink" "$dblink.sig"
687 ln -s "$filename" "$dblink" 2>/dev
/null || \
688 ln "$filename" "$dblink" 2>/dev
/null || \
689 cp "$REPO_DB_FILE" "$dblink"
690 if [[ -f "$REPO_DB_FILE.sig" ]]; then
691 ln -s "$filename.sig" "$dblink.sig" 2>/dev
/null || \
692 ln "$filename.sig" "$dblink.sig" 2>/dev
/null || \
693 cp "$REPO_DB_FILE.sig" "$dblink.sig"
696 msg
"$(gettext "No packages modified
, nothing to
do.
")"
701 # vim: set ts=2 sw=2 noet: