Line 0
Link Here
|
|
|
1 |
#!/bin/sh -f |
2 |
# |
3 |
# Copyright (c) 2009 |
4 |
# Dominic Fandrey <kamikaze@bsdforen.de> |
5 |
# |
6 |
# Redistribution and use in source and binary forms, with or without |
7 |
# modification, are permitted provided that the following conditions |
8 |
# are met: |
9 |
# 1. Redistributions of source code must retain the above copyright |
10 |
# notice, this list of conditions and the following disclaimer. |
11 |
# |
12 |
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
13 |
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
14 |
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
15 |
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
16 |
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
17 |
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
18 |
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
19 |
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
20 |
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
21 |
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
22 |
# |
23 |
|
24 |
readonly version=1.1 |
25 |
readonly name=pkg_upgrade |
26 |
|
27 |
# Error table. |
28 |
readonly ERR_LOCK=1 |
29 |
readonly ERR_ARG=2 |
30 |
readonly ERR_INDEX=3 |
31 |
readonly ERR_FETCH=4 |
32 |
readonly ERR_SORT=5 |
33 |
readonly ERR_BACKUP_MISS=6 |
34 |
readonly ERR_BACKUP_UNKNOWN=7 |
35 |
readonly ERR_INSTALL=8 |
36 |
readonly ERR_USER=9 |
37 |
readonly ERR_TERM=10 |
38 |
readonly ERR_PACKAGE_FORMAT=11 |
39 |
readonly ERR_CONFLICT=12 |
40 |
|
41 |
# Constant assignments. |
42 |
readonly logfile="%%VAR%%/log/$name.log" |
43 |
readonly pid=$$ |
44 |
|
45 |
# Get some environment variables from uma. This includes PACKAGESITE, |
46 |
# TMPDIR and PKG_INDEX. |
47 |
eval "$(uma env $pid)" |
48 |
|
49 |
# The remote package repository, derived from PACKAGESITE. |
50 |
# If this matches the PACKAGES environment variable all downloading operations |
51 |
# will be omitted. |
52 |
readonly packagerepos="${PACKAGESITE%/*?}" |
53 |
|
54 |
# Environment variables. |
55 |
: ${PACKAGES="$(make -V PACKAGES -f /usr/share/mk/bsd.port.mk 2> /dev/null)"} |
56 |
PACKAGES="${PACKAGES:-%%PORTS%%/packages}" |
57 |
: ${PKG_DBDIR=%%VAR%%/db/pkg} |
58 |
: ${TMPDIR=%%TMP%%} |
59 |
: ${PKG_TMPDIR=$TMPDIR} |
60 |
|
61 |
# This is where backup packages will be stored. |
62 |
readonly packagebackup="$PACKAGES/$name-backup" |
63 |
# This is where the download manager will listen for messages. |
64 |
readonly queueMessages="$TMPDIR/pkg_upgrade.messages.queue" |
65 |
|
66 |
# Export environment variables to ensure that every tool uses the same ones. |
67 |
export ARCH PACKAGEROOT PACKAGESITE FTP_TIMEOUT PKG_INDEX |
68 |
export PACKAGEROOT_MIRRORS PACKAGESITE_MIRRORS |
69 |
export PACKAGES PKG_DBDIR TMPDIR PKG_TMPDIR |
70 |
|
71 |
# Direct index access. |
72 |
readonly IDX_PKG=0 |
73 |
readonly IDX_ORIGIN=1 |
74 |
readonly IDX_PREFIX=2 |
75 |
readonly IDX_COMMENT=3 |
76 |
readonly IDX_DESCRIPTION=4 |
77 |
readonly IDX_MAINTAINER=5 |
78 |
readonly IDX_CATEGORIES=6 |
79 |
readonly IDX_DIRECTDEPENDS=7 |
80 |
readonly IDX_DEPENDS=8 |
81 |
readonly IDX_WWW=9 |
82 |
readonly IDX_PERLVERSION=10 |
83 |
readonly IDX_PERLMODULES=11 |
84 |
|
85 |
# Input field seperator without spaces. |
86 |
IFS=' |
87 |
' |
88 |
|
89 |
# Parameter flags. |
90 |
pAll= |
91 |
pNoBackup= |
92 |
pClean= |
93 |
pExitOnConflict= |
94 |
pForce= |
95 |
pFetchOnly= |
96 |
pInteractive= |
97 |
pJobs= |
98 |
pListDiscarded= |
99 |
pNoActions= |
100 |
pNoLogging= |
101 |
pParanoid= |
102 |
pRecursive= |
103 |
pReplaceConflicts= |
104 |
pMoreRecursive= |
105 |
pUpwardRecursive= |
106 |
pMoreUpwardRecursive= |
107 |
pVerbose= |
108 |
|
109 |
# The categories for packages. |
110 |
older= |
111 |
newer= |
112 |
unindexed= |
113 |
multiple= |
114 |
error= |
115 |
|
116 |
# A cache for the pkgDepends function. |
117 |
dependsChecked= |
118 |
|
119 |
# The names of packages that do not have a verified download. |
120 |
pending= |
121 |
|
122 |
# |
123 |
# The list of packages to upgrade. |
124 |
# |
125 |
|
126 |
# <origin>;<newPackage> |
127 |
upgrade= |
128 |
upgradeDepends= |
129 |
upgradeDepending= |
130 |
|
131 |
# The <newOrgin>;<newPackage> part can also be found in $upgrade. |
132 |
# <newOrigin>;<newPackage>|<oldOrigin>;<oldPackage> |
133 |
replace= |
134 |
|
135 |
# A list of dependency substitutions for new packages. |
136 |
# <originalOrigin>;<originalName>|<newDependencyOrigin>;<newDependencyName> |
137 |
substituteDepends= |
138 |
|
139 |
# The current status line. |
140 |
status= |
141 |
|
142 |
# The ports directory as used in the index file. |
143 |
idxports= |
144 |
|
145 |
# |
146 |
# Table Of Functions |
147 |
# In order of appearance. |
148 |
# |
149 |
# getIndex() Fetch the latest INDEX |
150 |
# getLock() Acquire a lock |
151 |
# printStatus() Print status messages on the terminal |
152 |
# error() Terminate with an error message |
153 |
# warn() Print a warning on stderr |
154 |
# verbose() Print a message, but only in verbose mode |
155 |
# log() Log activity into a log file |
156 |
# getIdxEscape() Escape origins and packages for regular expressions |
157 |
# getIdxRows() Filter index rows with an escaped expression |
158 |
# getIdxRowsEscaped() Filter index rows with an expression |
159 |
# getIdxColumn() Get a certain column from index rows |
160 |
# pkgAll() Make a list of outdated packages |
161 |
# pkgDepends() Check dependencies |
162 |
# pkgDepending() Check upwards dependencies |
163 |
# pkgDependencies() Run all dependency checks |
164 |
# printProgress() Print numerical progress output |
165 |
# pkgSort() Sort packages by dependency |
166 |
# printTask() Print the tasks to perform for a package |
167 |
# pkgList() List all tasks in 'no actions' mode |
168 |
# pkgDownload() Download all required packages |
169 |
# pkgUpgrade() Upgrade all scheduled packages |
170 |
# substituteDepends() Adjust dependencies of upgraded packages |
171 |
# upgradePackage() Upgrade a given package |
172 |
# identifyPackage() Identify a package by a user given string |
173 |
# printHelp() Print program parameters and terminate |
174 |
# readParams() Read the command line parameters |
175 |
# readContents() Read the +CONTENTS of a package file |
176 |
# downloadManager() Start a background download manager |
177 |
# downloadManagerFetch() |
178 |
# Try to fetch a package from a mirror |
179 |
# downloadManagerMsgRetry() |
180 |
# Tell the download manager to retry a download |
181 |
# downloadManagerMsgFinished() |
182 |
# Tell the download manager a download has been completed |
183 |
# downloadManagerMsgRequest() |
184 |
# Request a download from the download manager |
185 |
# downloadManagerMsgExit() |
186 |
# Tell the download manager to terminate |
187 |
# validatePackage() Validate a downloaded package |
188 |
# |
189 |
|
190 |
|
191 |
# |
192 |
# Update the local copy of the index and start the download manager. |
193 |
# |
194 |
# @param idxports |
195 |
# This is set to the ports directory used in the index file. This is |
196 |
# required for many index operations. If already set the index is |
197 |
# assumed to be up to date and nothing is done. |
198 |
# @param pVerbose |
199 |
# Activate verbose output. |
200 |
# |
201 |
getIndex() { |
202 |
# The index has already been updated. |
203 |
if [ -n "$idxports" ]; then |
204 |
return 0 |
205 |
fi |
206 |
|
207 |
# Free the lock upon termination. |
208 |
trap "uma unlock $pid" EXIT |
209 |
|
210 |
# First acquire the lock. |
211 |
getLock |
212 |
|
213 |
verbose "Synchronize the local index copy with the package server." |
214 |
|
215 |
# Try to update the index. |
216 |
if ! uma $pVerbose fetch ftpindex $pid; then |
217 |
exit $ERR_INDEX |
218 |
fi |
219 |
|
220 |
# Set the ports directory used in the index. |
221 |
idxports="$(getIdxColumn $IDX_ORIGIN "$(head -n 1 "$PKG_INDEX")")" |
222 |
idxports="${idxports%/*/*}" |
223 |
|
224 |
# Start the download manager. |
225 |
downloadManager |
226 |
} |
227 |
|
228 |
# |
229 |
# Acquires the uma (Update Manager) lock. And spawns a process that locks |
230 |
# onto PKG_DBDIR to block the ports from messing with us. |
231 |
# |
232 |
getLock() { |
233 |
# Acquire the lock. |
234 |
if ! uma lock $pid; then |
235 |
if [ "$USER" != "root" ]; then |
236 |
error $ERR_LOCK "The command $name has to be run as root." |
237 |
else |
238 |
error $ERR_LOCK "The uma (Update MAnager) lock could not be acquired, it appears the package/ports infrastructure is in use." |
239 |
fi |
240 |
fi |
241 |
|
242 |
# Lock onto PKG_DBDIR to avoid ports getting into our way. |
243 |
# The ports tree locks onto PKG_DBDIR during install and deinstall. |
244 |
# Since it does not use uma we use this lock to make sure the ports |
245 |
# tree does not get into our way later. |
246 |
if ! lockf -kst 0 "$PKG_DBDIR" sh -c "lockf -k '$PKG_DBDIR' sh -c 'while kill -0 $pid 2> /dev/null; do sleep 2; done' &"; then |
247 |
error $ERR_LOCK "Locking $PKG_DBDIR failed, the ports tree might be in use." |
248 |
fi |
249 |
} |
250 |
|
251 |
# |
252 |
# Prints a status message to the terminal device /dev/tty. |
253 |
# |
254 |
# @param 1 |
255 |
# The message to print |
256 |
# @param status |
257 |
# The last printed message, used for clearing the status line before |
258 |
# printing a new status. |
259 |
# @param pClean |
260 |
# If set, do not print status messages. |
261 |
# |
262 |
printStatus() { |
263 |
test -n "$pClean" && return 0 |
264 |
printf "\r%${#status}s\r%s\r" '' "$1" > /dev/tty |
265 |
status="$1" |
266 |
} |
267 |
|
268 |
# |
269 |
# Exits with the given error and message on stderr. |
270 |
# |
271 |
# @param 1 |
272 |
# The error number to exit with. |
273 |
# @param 2 |
274 |
# The message to exit with. |
275 |
# |
276 |
error() { |
277 |
# Clear the status line. |
278 |
printStatus |
279 |
echo "$name: $2" 1>&2 |
280 |
exit "$1" |
281 |
} |
282 |
|
283 |
# |
284 |
# Writes a warning message to stderr. |
285 |
# |
286 |
# @param 1 |
287 |
# The message to write. |
288 |
# |
289 |
warn() { |
290 |
# Clear the status line. |
291 |
printStatus |
292 |
echo "$name: $1" 1>&2 |
293 |
} |
294 |
|
295 |
# |
296 |
# Outputs verbose messages on stdout. |
297 |
# |
298 |
# @param @ |
299 |
# All the parameters to be output. |
300 |
# @param pVerbose |
301 |
# If this is not set, do not output anything. |
302 |
# |
303 |
verbose() { |
304 |
test -z "$pVerbose" && return 0 |
305 |
echo "$@" |
306 |
} |
307 |
|
308 |
# |
309 |
# Logs the given message into a log file. |
310 |
# |
311 |
# The following format is used. |
312 |
# |
313 |
# <UTC timestamp> - <date> - (<error>|DONE): <message> |
314 |
# |
315 |
# UTC timestamp := The output of 'date -u '+%s' |
316 |
# date := The output of 'date' |
317 |
# |
318 |
# @param 1 |
319 |
# The error number for the log, if this is 0, the message will be |
320 |
# preceded by "DONE:" instead of "ERROR($1):". |
321 |
# @param 2 |
322 |
# The message to log. |
323 |
# @param logfile |
324 |
# The name of the file to log into. |
325 |
# @param pNoLogging |
326 |
# If set, logging is not performed. |
327 |
# |
328 |
log() { |
329 |
test -n "$pNoLogging" && return 0 |
330 |
|
331 |
if [ $1 -eq 0 ]; then |
332 |
echo "$(date -u '+%s') - $(date) - DONE: $2" >> $logfile |
333 |
else |
334 |
echo "$(date -u '+%s') - $(date) - ERROR($1): $2" >> $logfile |
335 |
fi |
336 |
} |
337 |
|
338 |
# |
339 |
# An escape function for package names fed to the getIdxColumn function. |
340 |
# This function reads from the standard input unless a file is named |
341 |
# in the parameters. |
342 |
# Note that the escaping is done for extended regular expressions, however |
343 |
# only characters that can appear in package names are escaped. |
344 |
# |
345 |
# @param @ |
346 |
# More parameters can be added to the sed command. |
347 |
# |
348 |
getIdxEscape() { |
349 |
sed -E -e 's/([+.])/\\\1/g' "$@" |
350 |
} |
351 |
|
352 |
# |
353 |
# Outputs all rows of the index that match a given pattern in a column. |
354 |
# The pattern should not match '|'. |
355 |
# |
356 |
# @param 1 |
357 |
# The column that has to match the pattern. |
358 |
# @param 2 |
359 |
# The pattern that has to be matched, an extended regular expression. |
360 |
# @param 3 |
361 |
# Optional, the rows to match against instead of using the index file. |
362 |
# |
363 |
getIdxRows() { |
364 |
if [ -z "$3" ]; then |
365 |
grep -E "^([^|]*\|){$1}($2)(\|.*)?\$" "$PKG_INDEX" |
366 |
else |
367 |
echo "$3" | grep -E "^([^|]*\|){$1}($2)(\|.*)?\$" |
368 |
fi |
369 |
} |
370 |
|
371 |
# |
372 |
# Outputs all rows of the index that match a given string. |
373 |
# The string should not contain '|'. |
374 |
# |
375 |
# @param 1 |
376 |
# The column that has to match the string. |
377 |
# @param 2 |
378 |
# The string that has to be matched. |
379 |
# @param 3 |
380 |
# Optional, the rows to match against instead of using the index file. |
381 |
# |
382 |
getIdxRowsEscaped() { |
383 |
getIdxRows $1 "$(echo "$2" | getIdxEscape)" "$3" |
384 |
} |
385 |
|
386 |
# |
387 |
# Outputs a column of each index row piped into it. |
388 |
# |
389 |
# @param 1 |
390 |
# The column to output. |
391 |
# @param 2 |
392 |
# The rows to output the columns from. |
393 |
# |
394 |
getIdxColumn() { |
395 |
echo "$2" | sed -E "s,^([^|]*\|){$1}([^|]*)\|.*,\2,1" |
396 |
} |
397 |
|
398 |
# |
399 |
# Stores all the packages not in sync with the index file in categories. |
400 |
# |
401 |
# @param older |
402 |
# The list of packages older than those in the index. |
403 |
# @param newer |
404 |
# The list of packages newer than those in the index. |
405 |
# @param unindexed |
406 |
# The list of packages not in the index. |
407 |
# @param multiple |
408 |
# The list of packages that have multiple index entries. |
409 |
# @param error |
410 |
# The list of packages with broken package database entries. |
411 |
# @param pForce |
412 |
# If set, register all installed packages in the index as outdated. |
413 |
# @param pAll |
414 |
# If set, add all outdated packages to the list of packages to upgrade. |
415 |
# @param pListDiscarded |
416 |
# If set, list all the packages that are ignored. |
417 |
# @param upgrade |
418 |
# The list to add packages to if pAll is set. |
419 |
# |
420 |
pkgAll() { |
421 |
local package pkgname origin operator row discarded |
422 |
|
423 |
# There's nothing to be done if all of the following conditions are |
424 |
# met: |
425 |
# - Nothing is yet listed for upgrading, so we do not need a list |
426 |
# of outdated packages for dependency checking. |
427 |
# - The updating of all packages is not requested. |
428 |
# - The listing of ignored (i.e. not indexed) packages is not |
429 |
# requested. |
430 |
test -z "$upgrade" -a -z "$pAll" -a -z "$pListDiscarded" && return 0 |
431 |
|
432 |
verbose "Make a list of outdated packages." |
433 |
|
434 |
printStatus "Reading version information of installed packages ..." |
435 |
|
436 |
if [ -n "$pForce" ]; then |
437 |
# In force mode it is assumed that all installed packages to |
438 |
# be found in the index are outdated. |
439 |
for package in $(pkg_version -Io "${PKG_INDEX}"); { |
440 |
origin="${package%% *}" |
441 |
row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" |
442 |
pkgname="$(getIdxColumn $IDX_PKG "$row")" |
443 |
printStatus "Checking <$pkgname>." |
444 |
operator="${package##* }" |
445 |
case "$operator" in |
446 |
'?') |
447 |
unindexed="$unindexed${unindexed:+$IFS}$origin" |
448 |
;; |
449 |
'!') |
450 |
error="$error${error:+$IFS}$origin" |
451 |
;; |
452 |
*) |
453 |
older="$older${older:+$IFS}$origin;$pkgname" |
454 |
;; |
455 |
esac |
456 |
} |
457 |
else |
458 |
# Categorize installed packages and their relations to the |
459 |
# index. |
460 |
for package in $(pkg_version -IoL = ${PKG_INDEX}); { |
461 |
origin="${package%% *}" |
462 |
row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" |
463 |
pkgname="$(getIdxColumn $IDX_PKG "$row")" |
464 |
printStatus "Checking <${pkgname:-$(pkg_info -qO $origin)}>." |
465 |
operator="${package##* }" |
466 |
case "$operator" in |
467 |
'<') |
468 |
older="$older${older:+$IFS}$origin;$pkgname" |
469 |
;; |
470 |
'>') |
471 |
newer="$newer${newer:+$IFS}$origin;$pkgname" |
472 |
;; |
473 |
'?') |
474 |
unindexed="$unindexed${unindexed:+$IFS}$origin" |
475 |
;; |
476 |
'*') |
477 |
multiple="$multiple${multiple:+$IFS}$origin" |
478 |
;; |
479 |
'!') |
480 |
error="$error${error:+$IFS}$origin" |
481 |
;; |
482 |
esac |
483 |
} |
484 |
fi |
485 |
|
486 |
printStatus "Assemble checked packages ..." |
487 |
|
488 |
# Remove packages to upgrade from the list of outdated packages. |
489 |
for package in $upgrade; { |
490 |
older="$(echo "$older" | grep -vx "$package")" |
491 |
} |
492 |
|
493 |
# Append outdated packages to the list of packages to update if all |
494 |
# packages are to be updated. |
495 |
if [ -n "$pAll" ]; then |
496 |
downloadManagerMsgRequest "$older" |
497 |
upgrade="$upgrade${older:+${upgrade:+$IFS}}$older" |
498 |
older= |
499 |
fi |
500 |
|
501 |
# Clear the status line. |
502 |
printStatus |
503 |
|
504 |
# Print the discarded packages. |
505 |
if [ -n "$pListDiscarded" ]; then |
506 |
verbose "List discarded packages." |
507 |
|
508 |
discarded="$unindexed$IFS$multipleIFS$error" |
509 |
discarded="$(echo "$discarded" | grep -vFx '' | sort -u)" |
510 |
|
511 |
test -n "$discarded" && echo "$discarded" |
512 |
fi |
513 |
} |
514 |
|
515 |
# |
516 |
# Adds all missing dependencies to the list of packages to upgrade. |
517 |
# |
518 |
# @param 1 |
519 |
# This is used to check the dependencies of newly added depending |
520 |
# packages. |
521 |
# @param upgrade |
522 |
# The primary list of packages to upgrade (read only). |
523 |
# @param upgradeDepends |
524 |
# The list to add packages to upgrade to. |
525 |
# @param older |
526 |
# The list of outdated packages. Packages for upgrading are removed from |
527 |
# it. |
528 |
# @param dependsChecked |
529 |
# A list of already checked dependencies, to avoid double checks. |
530 |
# @param pRecursive |
531 |
# If set, also add outdated dependencies to the upgrade list. |
532 |
# @param pMoreRecursive |
533 |
# If set, also update the dependencies of depending packages. |
534 |
# @param pForce |
535 |
# If set together with pRecursive, add all dependencies to the upgrade |
536 |
# list. |
537 |
# |
538 |
pkgDepends() { |
539 |
local pkgname package row rows depends origin escapedPkg upgradeList |
540 |
|
541 |
printStatus "Preparing dependency checks ..." |
542 |
|
543 |
# In thorough mode the depencies of depending packages are updated, too. |
544 |
upgradeList="${1:-$upgrade}" |
545 |
|
546 |
# Luckily packages know their indirect dependencies, too. This way |
547 |
# it is not necessary to check for dependencies recursively. |
548 |
depends= |
549 |
for package in $upgradeList; { |
550 |
row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/${package%;*}")" |
551 |
row="$(getIdxColumn $IDX_DEPENDS "$row")" |
552 |
depends="$depends${depends:+${row:+ }}$row" |
553 |
} |
554 |
|
555 |
# Reformat depends and throw out duplicates. |
556 |
depends="$( |
557 |
echo "$depends" | sed "s/ /\\$IFS/g" | sort -u |
558 |
)" |
559 |
|
560 |
# Do some prefiltering. |
561 |
rows="$(getIdxRowsEscaped $IDX_PKG "$(echo "$depends" | rs -TC\|)")" |
562 |
|
563 |
# Check for missing or outdated dependencies. |
564 |
for pkgname in $depends; { |
565 |
escapedPkg="$(echo "$pkgname" | getIdxEscape)" |
566 |
|
567 |
# Skip packages already checked. |
568 |
if echo "$dependsChecked" | grep -qFx "$pkgname"; then |
569 |
continue |
570 |
fi |
571 |
dependsChecked="$dependsChecked${dependsChecked:+$IFS}$pkgname" |
572 |
|
573 |
printStatus "Check dependency <$pkgname>." |
574 |
|
575 |
# Skip this if this package is already scheduled for updating. |
576 |
if echo "$upgrade${upgradeDepending:+$IFS$upgradeDepending}" | grep -qF ";$pkgname"; then |
577 |
continue |
578 |
fi |
579 |
|
580 |
row="$(getIdxRows $IDX_PKG "$escapedPkg" "$rows")" |
581 |
|
582 |
# If this package could not be identified this is an index |
583 |
# incosistency, that can only be ignored. |
584 |
if [ -z "$row" ]; then |
585 |
warn "Ignore index inconsistency, the dependency <$pkgname> is not in the index." 1>&2 |
586 |
continue |
587 |
fi |
588 |
|
589 |
origin="$(getIdxColumn $IDX_ORIGIN "$row")" |
590 |
origin="${origin#$idxports/}" |
591 |
package="$origin;$(getIdxColumn $IDX_PKG "$row")" |
592 |
|
593 |
# |
594 |
# Deal with dependencies according to set parameters. |
595 |
# |
596 |
if [ -z "$(pkg_info -qO "$origin")" ]; then |
597 |
# The depency is not installed. |
598 |
upgradeDepends="$upgradeDepends${upgradeDepends:+$IFS}$package" |
599 |
# Request a package download. |
600 |
downloadManagerMsgRequest "$package" |
601 |
elif [ -n "$pMoreRecursive" -o -n "$pRecursive" -a -z "$1" ]; then |
602 |
# Check whether the dependency is outdated. |
603 |
if echo "$older" | grep -qFx "$package"; then |
604 |
upgradeDepends="$upgradeDepends${upgradeDepends:+$IFS}$package" |
605 |
older="$(echo "$older" | grep -vFx "$package")" |
606 |
# Request a package download. |
607 |
downloadManagerMsgRequest "$package" |
608 |
fi |
609 |
fi |
610 |
} |
611 |
} |
612 |
|
613 |
# |
614 |
# Checks whether packages depending on the packages to update require updating. |
615 |
# |
616 |
# @param 1 |
617 |
# This is used to check the depending packages of newly added |
618 |
# dependencies. |
619 |
# @param older |
620 |
# The list of outdated packages. If pForce is set, this includes all |
621 |
# installed packages listed in the index. |
622 |
# @param upgrade |
623 |
# The primary list of packages to upgrade (read only). |
624 |
# @param upgradeDepending |
625 |
# The list of depending packages to upgrade. |
626 |
# @param pUpwardRecursive |
627 |
# If not set nothing is done. |
628 |
# @param pMoreUpwardRecursive |
629 |
# Also check the depending packages of depencencies. |
630 |
# @param pAll |
631 |
# If this is set do nothing. |
632 |
# |
633 |
pkgDepending() { |
634 |
# Without the upwardRecursive option this is completely |
635 |
# unnecessary. |
636 |
if [ -z "$pUpwardRecursive" ]; then |
637 |
return 0 |
638 |
fi |
639 |
|
640 |
# If all packages are already going to be upgraded, there is no |
641 |
# need for this. |
642 |
if [ -n "$pAll" ]; then |
643 |
return 0 |
644 |
fi |
645 |
|
646 |
# Only update depending packages of dependencies in thorough mode. |
647 |
if [ -n "$1" -a -z "$pMoreUpwardRecursive" ]; then |
648 |
return 0 |
649 |
fi |
650 |
|
651 |
local package pkgname origin row depends escapedPkg upgradeList |
652 |
|
653 |
printStatus "Preparing upwards dependency checks ..." |
654 |
|
655 |
# In thorough mode the depencies of depending packages are updated, too. |
656 |
upgradeList="${1:-$upgrade}" |
657 |
|
658 |
# Do some prefiltering. |
659 |
rows="$(getIdxRowsEscaped $IDX_ORIGIN "$( |
660 |
echo "$older" | rs -TC\| | sed -E "s'([^;|]*);[^|]*'$idxports/\1'g" |
661 |
)")" |
662 |
|
663 |
# For each outdated package, check whether it depends on a package |
664 |
# to upgrade. In force mode outdated packages are all packages, so |
665 |
# the difference does not have to be made here. |
666 |
for package in $older; { |
667 |
# Skip this if this package is already scheduled for updating. |
668 |
if echo "$upgrade${upgradeDepends:+$IFS$upgradeDepends}${upgradeDepending:+$IFS$upgradeDepending}" | grep -qFx "$package"; then |
669 |
continue |
670 |
fi |
671 |
|
672 |
printStatus "Check for upwards dependency <${package#*;}>." |
673 |
|
674 |
origin="${package%;*}" |
675 |
row="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" |
676 |
|
677 |
# Ignore unindexed packages. |
678 |
if [ -z "$row" ]; then |
679 |
continue |
680 |
fi |
681 |
|
682 |
depends="$(getIdxColumn $IDX_DEPENDS "$row")" |
683 |
|
684 |
# It has no dependencies, so it cannot depend on anything |
685 |
# in the upgrade list. |
686 |
if [ -z "$depends" ]; then |
687 |
continue |
688 |
fi |
689 |
|
690 |
# Reformat dependencies. |
691 |
depends="$(echo "$depends" | sed -Ee "s/([^ ]+)/;\1/g" -e "s/ /\\$IFS/g")" |
692 |
|
693 |
# Check every dependency for matching the upgrade packages. |
694 |
if echo "$upgradeList" | grep -qF "$depends"; then |
695 |
upgradeDepending="$upgradeDepending${upgradeDepending:+$IFS}$package" |
696 |
older="$(echo "$older" | grep -vFx "$package")" |
697 |
downloadManagerMsgRequest "$package" |
698 |
fi |
699 |
} |
700 |
} |
701 |
|
702 |
# |
703 |
# This function calls pkgDepending and pkgDepends until no new packages |
704 |
# show up for updating. All the clever stuff happens in those functions. |
705 |
# |
706 |
# @param upgrade |
707 |
# The list of packages to upgrade. |
708 |
# @param upgradeDepends |
709 |
# The list of dependencies to add to the list of packages to upgrade. |
710 |
# @param upgradeDepending |
711 |
# The list of depending packages to add to the list of packages |
712 |
# to upgrade. |
713 |
# |
714 |
pkgDependencies() { |
715 |
test -z "$upgrade" && return 0 |
716 |
|
717 |
verbose "Perform dependency checks." |
718 |
|
719 |
# Run the primary dependency checks. |
720 |
pkgDepending |
721 |
downloadManagerMsgRequest "$upgradeDepending" |
722 |
pkgDepends |
723 |
downloadManagerMsgRequest "$upgradeDepends" |
724 |
|
725 |
# The idea is to keep on checking until nothing new shows up. |
726 |
# Whether that is the case depends on the level of recursiveness. |
727 |
while [ -n "$upgradeDepends$upgradeDepending" ]; do |
728 |
if [ -n "$upgradeDepends" ]; then |
729 |
# Deal with packages depending on the updated packages. |
730 |
pkgDepending "$upgradeDepends" |
731 |
upgrade="$upgradeDepends$IFS$upgrade" |
732 |
upgradeDepends= |
733 |
fi |
734 |
|
735 |
if [ -n "$upgradeDepending" ]; then |
736 |
# Deal with missing or outdated dependencies. |
737 |
pkgDepends "$upgradeDepending" |
738 |
upgrade="$upgrade$IFS$upgradeDepending" |
739 |
upgradeDepending= |
740 |
fi |
741 |
done |
742 |
|
743 |
# Clear the status line. |
744 |
printStatus |
745 |
} |
746 |
|
747 |
# |
748 |
# Prints a progress message to the terminal device /dev/tty. |
749 |
# |
750 |
# @param 1 |
751 |
# Total amount of operations to do. |
752 |
# @param 2 |
753 |
# The amount of operations performed. |
754 |
# @param 3 |
755 |
# The name of the package that is currently operated on. |
756 |
# @param 4 |
757 |
# The text prepending the progress information. |
758 |
# @param status |
759 |
# The last printed message, used for clearing the status line before |
760 |
# printing a new status. |
761 |
# @param pClean |
762 |
# If set, do not print progress messages. |
763 |
# |
764 |
printProgress() { |
765 |
test -n "$pClean" && return 0 |
766 |
printf "\r%${#status}s\r$4 %${#1}s of %${#1}s (%3s%%) <$3>.\r" '' "$2" "$1" "$(($2 * 100 / $1))" > /dev/tty |
767 |
status="$4 $1 of $1 (100%) <$3>." |
768 |
} |
769 |
|
770 |
# |
771 |
# Sorts the packages to upgrade by dependency. |
772 |
# |
773 |
# The trick is to have a list of already sorted packages. Each package added |
774 |
# to the list is inserted right behind its last dependency already present |
775 |
# there. |
776 |
# Packages without any dependencies in the sorted list are prepended. This |
777 |
# way it is ensured that they end up before all already sorted packages |
778 |
# that depend on them, without additional checking. |
779 |
# |
780 |
# @param upgrade |
781 |
# The list of packages to sort. |
782 |
# @param pParanoid |
783 |
# If set, make cyclic dependency checks. |
784 |
# |
785 |
pkgSort() { |
786 |
local rows sorted package row depends dependency pkgname |
787 |
local totalCount count |
788 |
|
789 |
test -z "$upgrade" && return 0 |
790 |
|
791 |
verbose "Sort packages by dependency." |
792 |
|
793 |
printStatus "Prepare sorting of packages ..." |
794 |
|
795 |
# Limit rows to whatever is currently required. |
796 |
rows="$(getIdxRowsEscaped $IDX_ORIGIN "$( |
797 |
echo "$upgrade" | getIdxEscape -e 's/;.*//1' -e "s,^,$idxports/,1" | rs -TC\| |
798 |
)")" |
799 |
|
800 |
# The number of packages |
801 |
totalCount=$(($(echo "$upgrade" | wc -l))) |
802 |
count=0 |
803 |
|
804 |
# Sort each package into the list of sorted packages. |
805 |
sorted= |
806 |
for package in $upgrade; { |
807 |
count=$(($count + 1)) |
808 |
pkgname="${package#*;}" |
809 |
printProgress $totalCount $count "$pkgname" 'Sort' |
810 |
|
811 |
# Get the list of dependencies that should be updated before |
812 |
# the current package. |
813 |
row="$(getIdxRowsEscaped $IDX_PKG "$pkgname" "$rows")" |
814 |
depends="$(getIdxColumn $IDX_DEPENDS "$row" | sed -E "s/ /\\$IFS/g")" |
815 |
|
816 |
# Get the last matching dependency in the list. |
817 |
dependency="$(echo "$sorted" | grep -Fx "$depends" | tail -n 1)" |
818 |
|
819 |
# If there is no match, just prepend to the list. |
820 |
if [ -z "$dependency" ]; then |
821 |
sorted="$pkgname${sorted:+$IFS$sorted}" |
822 |
continue |
823 |
fi |
824 |
|
825 |
# Insert right behind the match. |
826 |
dependency="$(echo "$dependency" | getIdxEscape)" |
827 |
sorted="$(echo "$sorted" | sed -E "s/^$dependency$/$dependency\\$IFS$pkgname/1")" |
828 |
} |
829 |
|
830 |
# Perform optional cyclic dependency check. |
831 |
if [ -n "$pParanoid" ]; then |
832 |
printStatus "Validate sorting order ..." |
833 |
|
834 |
# Validate the sort order. |
835 |
count=0 |
836 |
for pkgname in $sorted; { |
837 |
count=$(($count + 1)) |
838 |
printProgress $totalCount $count "$pkgname" 'Validate' |
839 |
|
840 |
# Get the list of dependencies that should be updated before |
841 |
# the current package. |
842 |
row="$(getIdxRowsEscaped $IDX_PKG "$pkgname" "$rows")" |
843 |
depends="$(getIdxColumn $IDX_DEPENDS "$row" | sed -E "s/ /\\$IFS/g")" |
844 |
|
845 |
# Append the package to the list of dependencies to match. |
846 |
depends="${depends:+$depends$IFS}$pkgname" |
847 |
|
848 |
# Get the last match in the list. |
849 |
dependency="$(echo "$sorted" | grep -Fx "$depends" | tail -n 1)" |
850 |
# The last match has to be the package. |
851 |
if [ "$dependency" != "$pkgname" ]; then |
852 |
error $ERR_SORT "The package <$pkgname> was not sorted properly, a likely cause is a circular dependency." |
853 |
fi |
854 |
} |
855 |
fi |
856 |
|
857 |
printStatus "Assemble sorted packages ..." |
858 |
|
859 |
# Replace package names with <origin>;<package> pairs. |
860 |
for package in $upgrade; { |
861 |
pkgname="$(echo "${package#*;}" | getIdxEscape)" |
862 |
sorted="$(echo "$sorted" | sed -E "s'^$pkgname\$'$package'1")" |
863 |
} |
864 |
|
865 |
upgrade="$sorted" |
866 |
printStatus |
867 |
} |
868 |
|
869 |
# |
870 |
# Prints the update/replace/install task. |
871 |
# |
872 |
# @param 1 |
873 |
# The package to upgrade/install. |
874 |
# @param replace |
875 |
# The list of packages to replace. |
876 |
# |
877 |
printTask() { |
878 |
local package newPkgname newOrigin oldPkgname oldOrigin |
879 |
|
880 |
# Get the name and origin of the new package. |
881 |
newPkgname="${1#*;}" |
882 |
newOrigin="${1%;*}" |
883 |
|
884 |
# Look for a package the new one replaces. |
885 |
package="$(echo "$replace" | grep -F "$1|")" |
886 |
|
887 |
# Look for a package this one replaces. |
888 |
# The current package actually replaces another one. |
889 |
if [ -n "$package" ]; then |
890 |
# Get the name and origin of the old package. |
891 |
package="${package#*|}" |
892 |
oldPkgname="${package#*;}" |
893 |
oldOrigin="${package%;*}" |
894 |
|
895 |
echo "Replace <$oldPkgname> ($oldOrigin) with <$newPkgname> ($newOrigin)" |
896 |
return 0 |
897 |
fi |
898 |
|
899 |
# Check whether there's an old version of this package around. |
900 |
package="$(pkg_info -qO "$newOrigin")" |
901 |
|
902 |
# An older package with this origin is installed. |
903 |
if [ -n "$package" ]; then |
904 |
echo "Update <$package> to <$newPkgname> ($newOrigin)" |
905 |
return 0 |
906 |
fi |
907 |
|
908 |
# Aparently this package will be newly installed. |
909 |
echo "Install <$newPkgname> ($newOrigin)" |
910 |
} |
911 |
|
912 |
# |
913 |
# List the packages that are going to be upgraded, installed and replaced. |
914 |
# If the 'no actions' mode is active. |
915 |
# |
916 |
# @param upgrade |
917 |
# The list of packages to upgrade. |
918 |
# @param pNoActions |
919 |
# Print the list of tasks. |
920 |
# |
921 |
pkgList() { |
922 |
# Only list packages in "no actions" mode. |
923 |
test -z "$pNoActions" && return 0 |
924 |
|
925 |
test -z "$upgrade" && return 0 |
926 |
|
927 |
local package |
928 |
|
929 |
verbose "The following packages will be updated:" |
930 |
|
931 |
for package in $upgrade; { |
932 |
printTask "$package" |
933 |
} |
934 |
} |
935 |
|
936 |
# |
937 |
# Wait for downloaded packages and validate them. |
938 |
# |
939 |
# @param upgrade |
940 |
# The list of packages to download. |
941 |
# @param pending |
942 |
# The list of pending downloads. |
943 |
# @param packagerepos |
944 |
# The location of the remote package repository (derived from |
945 |
# PACKAGESITE). If this is identical with the local repository, |
946 |
# the download manager was not started. |
947 |
# @param pNoActions |
948 |
# Do not download anything. |
949 |
# |
950 |
pkgDownload() { |
951 |
test -n "$pNoActions" && return 0 |
952 |
|
953 |
test -z "$upgrade" && return 0 |
954 |
|
955 |
local package total count line |
956 |
|
957 |
verbose "Validate downloaded packages." |
958 |
|
959 |
printStatus "Waiting for downloads ..." |
960 |
|
961 |
# Create a list of the package names to validate. |
962 |
# Entries are removed from this list by validatePackage(). |
963 |
pending="$(echo "$upgrade" | sed 's/.*;//1')" |
964 |
|
965 |
# The total number of packages to validate. |
966 |
total="$(($(echo "$upgrade" | wc -l)))" |
967 |
|
968 |
# Check whether the download manager is available. |
969 |
if [ "$PACKAGES" = "$packagerepos" ]; then |
970 |
# |
971 |
# The local repository is identical with the remote repository |
972 |
# so the assumption is all packages should already be there. |
973 |
# |
974 |
|
975 |
# Validate all packages. |
976 |
for package in $pending; { |
977 |
count=$(($count + 1)) |
978 |
printProgress $total $count "$package" "Validate" |
979 |
validatePackage "$package" |
980 |
} |
981 |
else |
982 |
# |
983 |
# The download manager is available, so hang on to its message |
984 |
# queue and proceed with validating as packages are finished. |
985 |
# |
986 |
count=0 |
987 |
|
988 |
while [ -n "$pending" ]; do |
989 |
read line |
990 |
case "$line" in |
991 |
finished:*) |
992 |
count=$(($count + 1)) |
993 |
package="${line##*;}" |
994 |
printProgress $total $count "$package" "Validate" |
995 |
validatePackage "$package" |
996 |
;; |
997 |
esac |
998 |
done < "$queueMessages" |
999 |
|
1000 |
# Stop the download manager. |
1001 |
downloadManagerMsgExit |
1002 |
fi |
1003 |
|
1004 |
# Clear the status line. |
1005 |
printStatus |
1006 |
} |
1007 |
|
1008 |
# |
1009 |
# Upgrade each package. |
1010 |
# |
1011 |
# @param upgrade |
1012 |
# The list of packages to upgrade. |
1013 |
# @param conflictReplace |
1014 |
# This list is reset for conflict handling. |
1015 |
# @param pNoActions |
1016 |
# Do not update anything. |
1017 |
# @param pFetchOnly |
1018 |
# Do not update anything. |
1019 |
# |
1020 |
pkgUpgrade() { |
1021 |
test -n "$pNoActions" -o -n "$pFetchOnly" && return 0 |
1022 |
|
1023 |
test -z "$upgrade" && return 0 |
1024 |
|
1025 |
local package |
1026 |
|
1027 |
verbose "Install $(($(echo "$upgrade" | wc -l))) package(s)." |
1028 |
|
1029 |
for package in $upgrade; { |
1030 |
upgradePackage "$package" |
1031 |
} |
1032 |
} |
1033 |
|
1034 |
# |
1035 |
# To handle conflicts this function removes dependencies from a given package |
1036 |
# and appends one or more new ones to take their place. Also the +REQUIRED_BY |
1037 |
# files of the appended dependencies are updated. |
1038 |
# |
1039 |
# @param 1 |
1040 |
# The name of the package to which to apply the substitutions. |
1041 |
# @param substituteDepends |
1042 |
# The list of dependency substitutions that should take place. |
1043 |
# |
1044 |
substituteDepends() { |
1045 |
# End here if there's nothing to substitute. |
1046 |
test -z "$substituteDepends" && return 0 |
1047 |
|
1048 |
local line originalOrigin originalPkgname newOrigin newPkgname |
1049 |
local contents append remove requiredBy |
1050 |
|
1051 |
printStatus "Adjust the dependencies of <$1> ..." |
1052 |
|
1053 |
# Get the contents file. |
1054 |
contents="$(cat "$PKG_DBDIR/$1/+CONTENTS")" |
1055 |
|
1056 |
# Because there can be several substitutions for a single package |
1057 |
# the new ones will be added to the end of the +CONTENTS file and all |
1058 |
# the matches will be removed later. |
1059 |
append= |
1060 |
remove= |
1061 |
for line in $substituteDepends; { |
1062 |
# Get original origin and package name from the line. |
1063 |
originalOrigin="${line%%;*}" |
1064 |
originalPkgname="${line%|*}" |
1065 |
originalPkgname="${originalPkgname#*;}" |
1066 |
|
1067 |
# Continue with the next line if this one does not match. |
1068 |
if ! echo "$contents" | grep -qFx "@pkgdep $originalPkgname"; then |
1069 |
continue |
1070 |
fi |
1071 |
|
1072 |
# Get new origin and package name from the line. |
1073 |
newOrigin="${line#*|}" |
1074 |
newPkgname="${newOrigin#*;}" |
1075 |
newOrigin="${newOrigin%;*}" |
1076 |
|
1077 |
warn "Add dependency <$newPkgname> ($newOrigin)." |
1078 |
|
1079 |
# Remember what to append and what to remove. |
1080 |
remove="${remove:+$remove$IFS}@pkgdep $originalPkgname$IFS@comment DEPORIGIN:$originalOrigin" |
1081 |
# Just for the very unlikely case that two dependencies get |
1082 |
# replaced for conflicting with the same package, check that |
1083 |
# a dependency is not added twice. |
1084 |
if ! echo "$append" | grep -qFx "@pkgdep $newPkgname"; then |
1085 |
append="$append$IFS@pkgdep $newPkgname$IFS@comment DEPORIGIN:$newOrigin" |
1086 |
fi |
1087 |
|
1088 |
# Make an entry for the package in the +REQUIRED_BY file of |
1089 |
# of the dependency to append. |
1090 |
requiredBy="$(cat "$PKG_DBDIR/$newPkgname/+REQUIRED_BY" 2> /dev/null)" |
1091 |
requiredBy="${requiredBy:+$requiredBy$IFS}$1" |
1092 |
echo "$requiredBy" | sort -u > "$PKG_DBDIR/$newPkgname/+REQUIRED_BY" |
1093 |
} |
1094 |
|
1095 |
# Remove the original dependency entries. |
1096 |
contents="$(echo "$contents" | grep -vFx "$remove")" |
1097 |
# Write the new file. Note that $append always starts with a newline. |
1098 |
echo "$contents$append" > "$PKG_DBDIR/$1/+CONTENTS" |
1099 |
} |
1100 |
|
1101 |
# |
1102 |
# Install the given package. This is where the magic happens. |
1103 |
# |
1104 |
# @param replace |
1105 |
# The list of packages to replace (read only). |
1106 |
# @param substituteDepends |
1107 |
# A list of dependency substitutions that should take place for each |
1108 |
# newly installed package to resolve conflicting packages. |
1109 |
# @param packagebackup |
1110 |
# The location for backup packages. This is derived from PACKAGES. |
1111 |
# @param pNoBackup |
1112 |
# If set, delete backups after successful completion. |
1113 |
# |
1114 |
upgradePackage() { |
1115 |
local task targetPackage targetPkgname targetOrigin package replace |
1116 |
local escapedPkg removePackages origin file conflict conflicting |
1117 |
local replacePkgdep requiredBy count |
1118 |
local signal |
1119 |
|
1120 |
# Get a string with the current upgrade task. |
1121 |
task="$(printTask "$1")" |
1122 |
echo "===> $task" |
1123 |
|
1124 |
targetPackage="$1" |
1125 |
targetPkgname="${1#*;}" |
1126 |
targetOrigin="${1%;*}" |
1127 |
|
1128 |
printStatus "Prepare installation of <$targetPkgname> ..." |
1129 |
|
1130 |
# Get the packages to replace with this one. Several packages can be |
1131 |
# replaced with a single one. |
1132 |
escapedPkg="$(echo "$targetPackage" | getIdxEscape)" |
1133 |
replace="$(echo "$replace" | grep -Ex "$escapedPkg\|.*" | sed -E "s'^$escapedPkg\|''1")" |
1134 |
|
1135 |
# Append the current package to the list of packages to replace. |
1136 |
replace="${replace:+$replace$IFS}$targetPackage" |
1137 |
|
1138 |
# Create the list of outdated packages that have to be backed up |
1139 |
# and for which pkgdb adjustments have to be made after successful |
1140 |
# installation of the new package. |
1141 |
# Also create the necessary sed expressions to update the |
1142 |
# package database. |
1143 |
removePackages= |
1144 |
replacePkgdep= |
1145 |
for package in $replace; { |
1146 |
origin="${package%;*}" |
1147 |
package="$(pkg_info -qO "$origin")" |
1148 |
test -z "$package" && continue |
1149 |
removePackages="$removePackages${removePackages:+$IFS}$package" |
1150 |
package="$(echo "$package" | getIdxEscape)" |
1151 |
replacePkgdep="$replacePkgdep -e 's|^@pkgdep $package\$|@pkgdep $targetPkgname|1'" |
1152 |
if [ "$origin" != "$targetOrigin" ]; then |
1153 |
replacePkgdep="$replacePkgdep -e 's|^@comment DEPORIGIN: $origin\$|@comment DEPORIGIN:$targetOrigin|1'" |
1154 |
fi |
1155 |
|
1156 |
} |
1157 |
|
1158 |
# Get a list of conflicting packages. The conflicts list is |
1159 |
# provided by readContents(). |
1160 |
readContents "$PACKAGES/All/$targetPkgname.tbz" |
1161 |
conflicting= |
1162 |
for conflict in $conflicts; { |
1163 |
# Match the conflict pattern against installed packages. |
1164 |
for conflict in $(pkg_info -E "$conflict"); { |
1165 |
escapedPkg="$(echo "$conflict" | getIdxEscape)" |
1166 |
# Only add to the conflicting list if the conflicting |
1167 |
# package is not in the list of packages to replace. |
1168 |
if ! echo "$removePackages" | grep -qEx "$escapedPkg"; then |
1169 |
conflicting="${conflicting:+$conflicting$IFS}$conflict" |
1170 |
fi |
1171 |
} |
1172 |
} |
1173 |
# Remove duplicated entries. |
1174 |
conflicting="$(echo "$conflicting" | sort -u)" |
1175 |
|
1176 |
# Check whether any conflicts were found. |
1177 |
if [ -n "$conflicting" ]; then |
1178 |
# What happens now depends on the user preferences. |
1179 |
if [ -n "$pExitOnConflict" ]; then |
1180 |
# The user has chosen to bail out when a conflict |
1181 |
# occurs. |
1182 |
log $ERR_CONFLICT "$task" |
1183 |
error $ERR_CONFLICT "The package <$targetPkgname> conflicts with the following packages:$IFS$conflicting" |
1184 |
elif [ -n "$pReplaceConflicts" ]; then |
1185 |
# The user has chosen that conflicting packages should |
1186 |
# be replaced as if they were explicitly listed for |
1187 |
# replacing. |
1188 |
conflicts= |
1189 |
for package in $conflicting; { |
1190 |
warn "The package <$package> conflicts with <$targetPkgname> and will be replaced." |
1191 |
removePackages="$removePackages${removePackages:+$IFS}$package" |
1192 |
origin="$(pkg_info -qo "$package")" |
1193 |
# The next line is just for prettier log output. |
1194 |
conflicts="${conflicts:+$conflicts, }<$package> ($origin)" |
1195 |
package="$(echo "$package" | getIdxEscape)" |
1196 |
replacePkgdep="$replacePkgdep -e 's|^@pkgdep $package\$|@pkgdep $targetPkgname|1'" |
1197 |
if [ "$origin" != "$targetOrigin" ]; then |
1198 |
replacePkgdep="$replacePkgdep -e 's|^@comment DEPORIGIN: $origin\$|@comment DEPORIGIN:$targetOrigin|1'" |
1199 |
fi |
1200 |
} |
1201 |
log 0 "Conflict <$targetPkgname> ($targetOrigin) remove package(s) $conflicts" |
1202 |
else |
1203 |
# The default action is to assume that the conflicting |
1204 |
# packages fulfill the required functionality. |
1205 |
conflicts= |
1206 |
for package in $conflicting; { |
1207 |
warn "The package <$targetPkgname> will not be installed in favour of <$package>, because they conflict." |
1208 |
origin="$(pkg_info -qo "$package")" |
1209 |
# Record the necessary substitutions. |
1210 |
# TODO: Later versions will have to store this |
1211 |
# for resume. |
1212 |
substituteDepends="${substituteDepends:+$substituteDepends$IFS}$targetPackage|$origin;$package" |
1213 |
# This is just for prettier log output. |
1214 |
conflicts="${conflicts:+$conflicts, }<$package> ($origin)" |
1215 |
} |
1216 |
# Log the conflict resolution. |
1217 |
log 0 "Conflict <$targetPkgname> ($targetOrigin) favour package(s) $conflicts" |
1218 |
# Skip to the next package. |
1219 |
return 0 |
1220 |
fi |
1221 |
fi |
1222 |
|
1223 |
# Backup packages. |
1224 |
mkdir -p "$packagebackup" |
1225 |
for package in $removePackages; { |
1226 |
printStatus "Backup <$package>." |
1227 |
pkg_create -b "$package" "$packagebackup/$package" |
1228 |
case $? in |
1229 |
0) |
1230 |
# Everything went well. |
1231 |
;; |
1232 |
1) |
1233 |
# If this happens someone's been messing with |
1234 |
# the packages just milliseconds ago. |
1235 |
log $ERR_BACKUP_MISS "$task" |
1236 |
error $ERR_BACKUP_MISS "The backup of <$package> failed. The package is missing." |
1237 |
;; |
1238 |
2) |
1239 |
# Fortunately pkg_create backs up as much as |
1240 |
# as is possible. That the backup (and hence |
1241 |
# the present package) is incomplete is all |
1242 |
# the more reason to upgrade. |
1243 |
# I do not understand why portmaster is |
1244 |
# interactive in this case. |
1245 |
warn "Ignoring incomplete backup of <$package>." |
1246 |
;; |
1247 |
*) |
1248 |
# Well, I've got no idea at all what else |
1249 |
# could go wrong. Too bad the return codes |
1250 |
# of pkg_create are not documented. |
1251 |
log $ERR_BACKUP_UNKNOWN "$task" |
1252 |
error $ERR_BACKUP_UNKNOWN "The backup of <$package> failed for unknown reasons." |
1253 |
;; |
1254 |
esac |
1255 |
} |
1256 |
|
1257 |
# Block SIGINT (CTRL-C), because that would really wrack havoc upon |
1258 |
# the package database in the following section. |
1259 |
signal= |
1260 |
trap "signal=$ERR_USER" sigint |
1261 |
trap "signal=$ERR_TERM" sigterm |
1262 |
|
1263 |
# Delete packages. |
1264 |
requiredBy= |
1265 |
count=-1 |
1266 |
for package in $removePackages; { |
1267 |
printStatus "Delete <$package>." |
1268 |
# Remember +REQUIRED_BY contents for roll-back. |
1269 |
count=$(($count + 1)) |
1270 |
local "requiredBy$count" |
1271 |
setvar "requiredBy$count" "$(cat "$PKG_DBDIR/$package/+REQUIRED_BY" 2> /dev/null)" |
1272 |
# Remember +REQUIRED_BY contents for the new package. |
1273 |
requiredBy="${requiredBy:+$requiredBy$IFS}$(cat "$PKG_DBDIR/$package/+REQUIRED_BY" 2> /dev/null)" |
1274 |
# Finally delete the package. |
1275 |
pkg_delete -f "$package" |
1276 |
} |
1277 |
|
1278 |
# Update the package database. |
1279 |
printStatus "Update package database for <$targetPkgname>." |
1280 |
if [ -n "$replacePkgdep" ]; then |
1281 |
for file in $(find "$PKG_DBDIR" -name '+CONTENTS'); { |
1282 |
eval "sed -Ei '.$name' $replacePkgdep '$file'" |
1283 |
} |
1284 |
fi |
1285 |
|
1286 |
# If an old version of this package was favoured in a conflict, |
1287 |
# the substituteDepends list has to be changed. |
1288 |
substituteDepends="$(echo "$substituteDepends" | sed "s'\|$targetOrigin;.*'|$targetPackage'1")" |
1289 |
|
1290 |
# Try to install the new package. |
1291 |
printStatus "Install <$targetPkgname>." |
1292 |
if ! env PKG_PATH="$PACKAGES/All" pkg_add -f "$targetPkgname"; then |
1293 |
# Installation went wrong, roll back! |
1294 |
printStatus "Roll back changes for <$targetPkgname>." |
1295 |
for file in $(find "$PKG_DBDIR" -name "*.$name"); { |
1296 |
mv -f "$file" "${file%.$name}" |
1297 |
} |
1298 |
count=-1 |
1299 |
for package in $removePackages; { |
1300 |
# Restore package. |
1301 |
env PKG_PATH="$packagebackup" pkg_add -f "$package" |
1302 |
# Recover +REQUIRED_BY file. |
1303 |
count=$(($count + 1)) |
1304 |
eval "echo \"\$requiredBy$count\"" > "$PKG_DBDIR/$package/+REQUIRED_BY" |
1305 |
# Remove the backup if set. |
1306 |
test -n "$pNoBackup" && rm "$packagebackup/$package.tbz" |
1307 |
} |
1308 |
log $ERR_INSTALL "$task" |
1309 |
error $ERR_INSTALL "The installation of <$targetPkgname> failed." |
1310 |
fi |
1311 |
|
1312 |
# Add the +REQUIRED_BY contents of all deleted packages to the |
1313 |
# +REQUIRED_BY file of the new one. |
1314 |
requiredBy="$(echo "$(cat "$PKG_DBDIR/$targetPkgname/+REQUIRED_BY" 2> /dev/null)$IFS$requiredBy" | grep -vFx '' | sort -u)" |
1315 |
echo "$requiredBy" > "$PKG_DBDIR/$targetPkgname/+REQUIRED_BY" |
1316 |
|
1317 |
# Make dependency substitutions from conflict resolving. |
1318 |
substituteDepends "$targetPkgname" |
1319 |
|
1320 |
# Log successful completion of the task. |
1321 |
log 0 "$task" |
1322 |
|
1323 |
# Remove backups if set. |
1324 |
if [ -n "$pNoBackup" ]; then |
1325 |
for package in $removePackages; { |
1326 |
printStatus "Remove backup of <$package>." |
1327 |
rm "$packagebackup/$package.tbz" |
1328 |
} |
1329 |
fi |
1330 |
|
1331 |
# Remove package database backups. |
1332 |
# TODO: Later versions will instead store them to allow a rollback. |
1333 |
printStatus "Remove database backups for <$targetPkgname>." |
1334 |
find "$PKG_DBDIR" -name "*.$name" -exec rm \{\} \; |
1335 |
|
1336 |
# Clear the status line. |
1337 |
printStatus |
1338 |
echo "=> $task succeeded" |
1339 |
|
1340 |
# Bail out if SIGINT or SIGTERM were encountered. |
1341 |
if [ -n "$signal" ]; then |
1342 |
error $signal "The process was interrupted." |
1343 |
fi |
1344 |
|
1345 |
# Reactivate default signal handlers. |
1346 |
trap - sigint sigterm |
1347 |
} |
1348 |
|
1349 |
# |
1350 |
# Identify the package by a given string. Outputs the origin of all matched |
1351 |
# packages, as well as the package name of the newest available package. |
1352 |
# The output is in the following shape: |
1353 |
# <origin>;<package> |
1354 |
# |
1355 |
# The shell wildcards '*' and '?' are supported. |
1356 |
# Origin and package names with wildcards are matched against installed |
1357 |
# packages. Unambiguous package names and origins are matched against the |
1358 |
# index. |
1359 |
# |
1360 |
# @param 1 |
1361 |
# The package identifier to find matches for. |
1362 |
# |
1363 |
identifyPackage() { |
1364 |
local packages package mangledPackage rows matchingRows mangledRows |
1365 |
local origins origin guess escapedPkg |
1366 |
|
1367 |
# Check for wildcards. |
1368 |
guess= |
1369 |
if echo "$1" | grep -qE '\*|\?|\[.*]'; then |
1370 |
guess=1 |
1371 |
fi |
1372 |
package="$1" |
1373 |
|
1374 |
# Distuinguish between origins and packages. |
1375 |
case "$package" in |
1376 |
*/*) |
1377 |
# An origin has been given. |
1378 |
if [ -n "$guess" ]; then |
1379 |
# Wildcards present, match against installed |
1380 |
# packages. |
1381 |
|
1382 |
# Get all matching packages. |
1383 |
packages="$(pkg_info -qO "$package")" |
1384 |
|
1385 |
# Convert for use in a regular expression. |
1386 |
package="$(echo "$package" | getIdxEscape -e 's/\*/[^|]*/g' -e 's/\?/[^|]/g')" |
1387 |
# Get rows matching the given package origin. |
1388 |
# This is a performance tweak, so the whole |
1389 |
# index will not have to be parsed in the |
1390 |
# following output loop. |
1391 |
rows="$(getIdxRows $IDX_ORIGIN "$idxports/$package")" |
1392 |
|
1393 |
# Output all matching packages. |
1394 |
for package in $packages; { |
1395 |
# Get the origin. |
1396 |
origin="$(pkg_info -qo "$package")" |
1397 |
|
1398 |
# Match this package origin against the |
1399 |
# previously filtered rows. |
1400 |
package="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" |
1401 |
# Get the package name of the newest |
1402 |
# package from the index. |
1403 |
package="$(getIdxColumn $IDX_PKG "$package")" |
1404 |
# Output origin/package pair. |
1405 |
echo "$origin;$package" |
1406 |
} |
1407 |
|
1408 |
# If no matches have been found, terminate. |
1409 |
if [ -z "$packages" ]; then |
1410 |
error $ERR_ARG "Package origin <$package> not matched by any installed package!" 1>&2 |
1411 |
fi |
1412 |
else |
1413 |
# There is an unambigious origin, match it |
1414 |
# against the index. |
1415 |
origin="$package" |
1416 |
# Get the index row. |
1417 |
rows="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" |
1418 |
# Get the package name column. |
1419 |
package="$(getIdxColumn $IDX_PKG "$rows")" |
1420 |
# Output origin/package pair, if a package for |
1421 |
# the given origin was found. |
1422 |
if [ -n "$package" ]; then |
1423 |
# Output origin/package pair. |
1424 |
echo "$origin;$package" |
1425 |
else |
1426 |
error $ERR_ARG "Package origin <$origin> not in index!" 1>&2 |
1427 |
fi |
1428 |
fi |
1429 |
;; |
1430 |
*) |
1431 |
# A package name has been given. |
1432 |
if [ -n "$guess" ]; then |
1433 |
# Wildcards present, match against installed |
1434 |
# packages. |
1435 |
|
1436 |
# Get the origins of matching packages. |
1437 |
origins="$(pkg_info -qo "$package")" |
1438 |
|
1439 |
# Prepare the package name for use in a |
1440 |
# regular expression. |
1441 |
package="$(echo "$package" | getIdxEscape -e 's/\*/[^|]*/g' -e 's/\?/[^|]/g')" |
1442 |
# Get rows matching the given package name. |
1443 |
# This is a performance tweak, so the whole |
1444 |
# index will not have to be parsed in the |
1445 |
# following output loop. |
1446 |
rows="$(getIdxRows $IDX_PKG "$package")" |
1447 |
# Output all matching packages. |
1448 |
for origin in $origins; { |
1449 |
# Get the index row for this origin. |
1450 |
package="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin" "$rows")" |
1451 |
# Get the latest package name from the |
1452 |
# index. |
1453 |
package="$(getIdxColumn $IDX_PKG "$package")" |
1454 |
# Output origin/package pair. |
1455 |
echo "$origin;$package" |
1456 |
} |
1457 |
|
1458 |
# If no matches have been found, terminate. |
1459 |
if [ -z "$origins" ]; then |
1460 |
error $ERR_ARG "Package identifier <$package> not matched!" 1>&2 |
1461 |
fi |
1462 |
else |
1463 |
# A package name without wildcards has been |
1464 |
# given. This is expected to either be an exact |
1465 |
# package name or a LATEST_LINK name. |
1466 |
|
1467 |
# TODO: This would be much better if |
1468 |
# LATEST_LINK was known. This is information |
1469 |
# simply missing in the index. |
1470 |
# To make up for this some guessing is done in |
1471 |
# case of no matches or more than one match. |
1472 |
# But this fails for apache13 and probably |
1473 |
# other packages as well. |
1474 |
|
1475 |
# First try whether it is the current version |
1476 |
# of a package. |
1477 |
origin="$(pkg_info -qo "$package" 2> /dev/null)" |
1478 |
if [ -n "$origin" ]; then |
1479 |
# Get the matching index rows. |
1480 |
rows="$(getIdxRowsEscaped $IDX_ORIGIN "$idxports/$origin")" |
1481 |
fi |
1482 |
|
1483 |
# If it's not a current version, match against |
1484 |
# the index. |
1485 |
if [ -z "$rows" ]; then |
1486 |
# Get the matching rows. This should be |
1487 |
# only one, but it won't be for ports |
1488 |
# that define a proprietary LATEST_LINK. |
1489 |
escapedPkg="$(echo "$package" | getIdxEscape)" |
1490 |
rows="$(getIdxRows $IDX_PKG "$escapedPkg(-[^-]+)?")" |
1491 |
fi |
1492 |
|
1493 |
# No match, start some guessing. |
1494 |
# This fails for packages with a version tail, |
1495 |
# which is just what is wanted. |
1496 |
if [ -z "$rows" ]; then |
1497 |
# Assume this is a LATEST_LINK kind |
1498 |
# package name and remove the trailing |
1499 |
# numbers. |
1500 |
mangledPackage="$(echo "$package" | sed -E 's/[0-9]+$//1')" |
1501 |
# Get the matching rows, this is likely |
1502 |
# to be too many (i.e. more than one). |
1503 |
rows="$(getIdxRows $IDX_PKG "$mangledPackage-[^-]+")" |
1504 |
fi |
1505 |
|
1506 |
# If there is more than one matching row, |
1507 |
# try to match against the origin. |
1508 |
if [ "$(($(echo "$rows" | wc -l)))" -gt "1" ]; then |
1509 |
# Match against the origin. |
1510 |
rows="$(getIdxRows $IDX_ORIGIN "[^|]*/$package" "$rows")" |
1511 |
|
1512 |
# If there is still more than one |
1513 |
# match, match against the origins |
1514 |
# of existing packages. |
1515 |
if [ "$(($(echo "$rows" | wc -l)))" -gt "1" ]; then |
1516 |
for origin in $(getIdxColumn $IDX_ORIGIN "$rows"); { |
1517 |
test -n "$(pkg_info -qO "$origin")" \ |
1518 |
&& matchingRows="$matchingRows${matchingRows:+$IFS}$(getIdxRowsEscaped $IDX_ORIGIN "$origin" "$rows")" |
1519 |
} |
1520 |
rows="$matchingRows" |
1521 |
fi |
1522 |
|
1523 |
# Either a single origin is matched or |
1524 |
# it's time to bail out and give up. |
1525 |
if [ "$(($(echo "$rows" | wc -l)))" -ne "1" ]; then |
1526 |
# The wrong amount of matches |
1527 |
# has occured. Bail out. |
1528 |
error $ERR_ARG "Package identifier <$package> not unambiguously matched!" 1>&2 |
1529 |
fi |
1530 |
fi |
1531 |
|
1532 |
# Output if a package has been matched. |
1533 |
if [ -n "$rows" ]; then |
1534 |
# Get the origin of the given package. |
1535 |
origin="$(getIdxColumn $IDX_ORIGIN "$rows")" |
1536 |
# Geth the package name. |
1537 |
package="$(getIdxColumn $IDX_PKG "$rows")" |
1538 |
# Output origin/package pair. |
1539 |
echo "${origin#$idxports/};$package" |
1540 |
else |
1541 |
error $ERR_ARG "Package identifier <$package> not in index!" 1>&2 |
1542 |
fi |
1543 |
fi |
1544 |
;; |
1545 |
esac |
1546 |
} |
1547 |
|
1548 |
# |
1549 |
# Prints the parameter list and terminates the program. |
1550 |
# |
1551 |
printHelp() { |
1552 |
printf "$name v$version |
1553 |
usage: |
1554 |
$name -h |
1555 |
$name -a [-b] [-bcCdfFlnpvX] [-o new existing] [update] [install] |
1556 |
$name [-bcCdfFlnpvX] [-r [-r]] [-R [-R]] [-o new existing] |
1557 |
%${#name}s [update] [install]\n" '' |
1558 |
exit 0 |
1559 |
} |
1560 |
|
1561 |
# |
1562 |
# Parse the command line parameters. |
1563 |
# |
1564 |
# @param upgrade |
1565 |
# A list of packages to upgrade. |
1566 |
# @param depth |
1567 |
# This is used by the function to store the recursion depth and |
1568 |
# should be unset when calling it. |
1569 |
# @param origin |
1570 |
# This is used by the function across differtent recursion depths to |
1571 |
# remember whether a package origin is expected. |
1572 |
# @param pAll |
1573 |
# Is set if all packages should be update. |
1574 |
# @param pNoBackup |
1575 |
# Is set if backups could not be fetched. |
1576 |
# @param pClean |
1577 |
# Is set to turn off status messages. |
1578 |
# @param pReplaceConflicts |
1579 |
# Is set to replace conflicting packages with new ones instead of |
1580 |
# leaving them alone. |
1581 |
# @param pExitOnConflict |
1582 |
# Is set to stop the program if a conflict is encountered. |
1583 |
# @param pForce |
1584 |
# Is set to force the update of packages that are not really updated. |
1585 |
# @param pFetchOnly |
1586 |
# Is set to only fetch packages instead of installing/upgrading them. |
1587 |
# @param pInteractive |
1588 |
# TODO: Reserved for future versions (resume/roll-back). |
1589 |
# @param pJobs |
1590 |
# TODO: Reserved for future versions (pkg_libchk tests). |
1591 |
# @param pListDiscarded |
1592 |
# Is set to activate the listing of packages that are ignored because |
1593 |
# they are not set in the INDEX. |
1594 |
# @param pNoActions |
1595 |
# Is set if no actions should be performed but a list of what would have |
1596 |
# been done should get printed. |
1597 |
# @param pNoLogging |
1598 |
# Turn off logging. |
1599 |
# @param pParanoid |
1600 |
# Is set to activate cyclic dependency checks. |
1601 |
# @param pRecursive |
1602 |
# Is set to activate updating of dependencies. |
1603 |
# @param pMoreRecursive |
1604 |
# Is set to activate updating of dependencies of depending packages. |
1605 |
# @param pUpwardRecursive |
1606 |
# Is set to activate updating of depending packages. |
1607 |
# @param pMoreUpwardRecursive |
1608 |
# Is set to activate updating of packages depending on dependencies. |
1609 |
# @param pVerbose |
1610 |
# Is set to activate informative output. |
1611 |
# |
1612 |
readParams() { |
1613 |
local arg package escapedPkg depth |
1614 |
# Store the recursion depth. Note that counting down is dealt with |
1615 |
# by making depth local. |
1616 |
depth=$((${depth:--1} + 1)) |
1617 |
|
1618 |
# This is used to remember whether the next parameter should |
1619 |
# be a replacing package or a packge to be replaced. |
1620 |
origin=${origin:-0} |
1621 |
|
1622 |
for arg { |
1623 |
# |
1624 |
# Handle package replacements. |
1625 |
# |
1626 |
if [ $origin -eq 1 ]; then |
1627 |
# Store the replacement. |
1628 |
package="$(identifyPackage "$arg")" || exit $? |
1629 |
if [ -z "$package" -o "$(($(echo "$package" | wc -l)))" -ne "1" ]; then |
1630 |
error $ERR_ARG "The package identifier <$arg> is not unambiguous." |
1631 |
fi |
1632 |
upgrade="$upgrade${upgrade:+$IFS}$package" |
1633 |
replace="$replace${replace:+$IFS}$package" |
1634 |
origin=2 |
1635 |
# Request the download. |
1636 |
downloadManagerMsgRequest "$package" |
1637 |
continue |
1638 |
fi |
1639 |
if [ $origin -eq 2 ]; then |
1640 |
# Store what to replace. |
1641 |
# This is taken from the package database not the index. |
1642 |
|
1643 |
case "$arg" in |
1644 |
*/*) |
1645 |
# Assume arg is an origin. |
1646 |
package="$(pkg_info -qO "$arg")" |
1647 |
package="$(pkg_info -qo "$package" 2> /dev/null);$package" |
1648 |
;; |
1649 |
*) |
1650 |
# Assume arg is a package identifier. |
1651 |
package="$(pkg_info -qo "$arg" 2> /dev/null);$(pkg_info -E "$arg" 2> /dev/null)" |
1652 |
|
1653 |
# Maybe arg is a package identifier |
1654 |
# without a version tail. |
1655 |
if [ "$package" = ";" ]; then |
1656 |
package="$(pkg_info -qo "$arg-*" 2> /dev/null);$(pkg_info -E "$arg-*" 2> /dev/null)" |
1657 |
fi |
1658 |
;; |
1659 |
esac |
1660 |
|
1661 |
# Arg is not installed. |
1662 |
if [ "$package" = ";" ]; then |
1663 |
error $ERR_ARG "The package <$arg> is not installed and thus cannot be replaced." |
1664 |
fi |
1665 |
# It appears arg is an identifier that is |
1666 |
# not unambiguous. |
1667 |
if [ "$(($(echo "$package" | wc -l)))" -ne "1" ]; then |
1668 |
error $ERR_ARG "The package identifier <$arg> is not unambiguous." |
1669 |
fi |
1670 |
# A package can only be replaced once. |
1671 |
escapedPkg="$(echo "$package" | getIdxEscape)" |
1672 |
if echo "$replace" | grep -qEx ".*\|$escapedPkg"; then |
1673 |
error $ERR_ARG "The package <$arg> is already listed for replacement." |
1674 |
fi |
1675 |
replace="$replace|$package" |
1676 |
origin=0 |
1677 |
continue |
1678 |
fi |
1679 |
|
1680 |
# |
1681 |
# Identify arguments. |
1682 |
# |
1683 |
case "$arg" in |
1684 |
"-a" | "--all") |
1685 |
pAll=1 |
1686 |
if [ -n "$pRecursive" ]; then |
1687 |
error $ERR_ARG "Recursiveness has no effect, because all packages are already selected for processing." |
1688 |
fi |
1689 |
if [ -n "$pUpwardRecursive" ]; then |
1690 |
error $ERR_ARG "Upward recursiveness has no effect, because all packages are already selected for processing." |
1691 |
fi |
1692 |
;; |
1693 |
"-b" | "--no-backup") |
1694 |
pNoBackup=1 |
1695 |
;; |
1696 |
"-c" | "--clean") |
1697 |
pClean=-c |
1698 |
;; |
1699 |
"-C" | "--replace-conflicts") |
1700 |
if [ -n "$pExitOnConflict" ]; then |
1701 |
error $ERR_ARG "The 'replace conflicts' and 'exit on conflict' modes are mutually exclusive." |
1702 |
fi |
1703 |
pReplaceConflicts=1 |
1704 |
;; |
1705 |
"-d" | "--list-discarded") |
1706 |
pListDiscarded=1 |
1707 |
;; |
1708 |
"-f" | "--force") |
1709 |
pForce=1 |
1710 |
;; |
1711 |
"-F" | "--fetch-only") |
1712 |
if [ -n "$pNoActions" ]; then |
1713 |
error $ERR_ARG "The 'no actions' and 'fetch only' modes are mutually exclusive." |
1714 |
fi |
1715 |
pFetchOnly=1 |
1716 |
;; |
1717 |
"-h" | "--help") |
1718 |
printHelp |
1719 |
;; |
1720 |
"-i" | "--interactive") |
1721 |
# TODO: not yet used |
1722 |
pInteractive=1 |
1723 |
;; |
1724 |
-j* | --jobs*) |
1725 |
# TODO: not yet used |
1726 |
pJobs="$arg" |
1727 |
if ! pkg_libchk "$pJobs" DUMMY/DUMMY 1>&2; then |
1728 |
exit $ERR_ARG |
1729 |
fi |
1730 |
;; |
1731 |
"-l" | "--no-logging") |
1732 |
pNoLogging=1 |
1733 |
;; |
1734 |
"-n" | "--no-actions") |
1735 |
if [ -n "$pFetchOnly" ]; then |
1736 |
error $ERR_ARG "The 'no actions' and 'fetch only' modes are mutually exclusive." |
1737 |
fi |
1738 |
pNoActions=1 |
1739 |
;; |
1740 |
"-o" | "--origin") |
1741 |
# Make sure the local index copy is up to date. |
1742 |
getIndex |
1743 |
origin=1 |
1744 |
;; |
1745 |
"-p" | "--paranoid") |
1746 |
pParanoid=1 |
1747 |
;; |
1748 |
"-r" | "--recursive") |
1749 |
if [ -n "$pMoreRecursive" ]; then |
1750 |
error $ERR_ARG "There are only two levels of recursiveness." |
1751 |
elif [ -n "$pRecursive" ]; then |
1752 |
pMoreRecursive=1 |
1753 |
else |
1754 |
pRecursive=1 |
1755 |
fi |
1756 |
if [ -n "$pAll" ]; then |
1757 |
error $ERR_ARG "Recursiveness has no effect, because all packages are already selected for processing." |
1758 |
fi |
1759 |
;; |
1760 |
"-R" | "--upward-recursive") |
1761 |
if [ -n "$pMoreUpwardRecursive" ]; then |
1762 |
error $ERR_ARG "There are only two levels of upward recursiveness." |
1763 |
elif [ -n "$pUpwardRecursive" ]; then |
1764 |
pMoreUpwardRecursive=1 |
1765 |
else |
1766 |
pUpwardRecursive=1 |
1767 |
fi |
1768 |
if [ -n "$pAll" ]; then |
1769 |
error $ERR_ARG "Upward recursiveness has no effect, because all packages are already selected for processing." |
1770 |
fi |
1771 |
;; |
1772 |
"-v" | "--verbose") |
1773 |
pVerbose=-v |
1774 |
;; |
1775 |
"-X" | "--exit-on-conflict") |
1776 |
if [ -n "$pReplaceConflicts" ]; then |
1777 |
error $ERR_ARG "The 'exit on conflict' and 'replace conflicts' modes are mutually exclusive." |
1778 |
fi |
1779 |
pExitOnConflict=1 |
1780 |
;; |
1781 |
-? | --*) |
1782 |
error $ERR_ARG "Unknown parameter \"$arg\"." |
1783 |
;; |
1784 |
-*) |
1785 |
# Split parmeters. |
1786 |
readParams "${arg%%${arg#-?}}" "-${arg#-?}" |
1787 |
;; |
1788 |
*) |
1789 |
# Make sure the local index copy is up to date. |
1790 |
getIndex |
1791 |
# Add package to the list of packages to |
1792 |
# upgrade/install. |
1793 |
package="$(identifyPackage "$arg")" || exit $? |
1794 |
upgrade="$upgrade${upgrade:+$IFS}$package" |
1795 |
# Request the download. |
1796 |
downloadManagerMsgRequest "$package" |
1797 |
;; |
1798 |
esac |
1799 |
} |
1800 |
|
1801 |
# |
1802 |
# Only perform the following steps if this is the root call |
1803 |
# to this function (recursion depth = 0). |
1804 |
# |
1805 |
if [ $depth -eq 0 ]; then |
1806 |
# |
1807 |
# Deal with missing parameters. |
1808 |
# |
1809 |
if [ $origin -eq 1 ]; then |
1810 |
error $ERR_ARG "Incomplete parameters, missing origin." |
1811 |
fi |
1812 |
if [ $origin -eq 2 ]; then |
1813 |
error $ERR_ARG "Incomplete parameters, missing package to replace." |
1814 |
fi |
1815 |
|
1816 |
# |
1817 |
# Deal with invalid levels of recursiveness. |
1818 |
# |
1819 |
if [ -n "$pMoreRecursive" -a -z "$pUpwardRecursive" ]; then |
1820 |
error $ERR_ARG "Thorough recursiveness can only be used in conjunction with upwards recursiveness." |
1821 |
fi |
1822 |
|
1823 |
# |
1824 |
# Remove duplicates in the list of packages to upgrade. |
1825 |
# |
1826 |
upgrade="$(echo "$upgrade" | sort -u)" |
1827 |
# Reset global variables. |
1828 |
origin= |
1829 |
fi |
1830 |
} |
1831 |
|
1832 |
# |
1833 |
# Reads all the required +CONTENTS information from a package. |
1834 |
# The information is stored in variables. |
1835 |
# |
1836 |
# @param 1 |
1837 |
# The name of the package file. |
1838 |
# @param pkgname |
1839 |
# The name of the package. |
1840 |
# @param origin |
1841 |
# The origin of the package. |
1842 |
# @param depends |
1843 |
# The dependencies of the package in the format "<origin>;<package>", |
1844 |
# in reverse order. |
1845 |
# @param conflicts |
1846 |
# A list of regular expressions that can be used to identify conflicting |
1847 |
# packages. |
1848 |
# |
1849 |
readContents() { |
1850 |
local contents line format |
1851 |
contents="$(tar -xOf "$1" '+CONTENTS')" |
1852 |
format= |
1853 |
|
1854 |
pkgname= |
1855 |
origin= |
1856 |
depends= |
1857 |
conflicts= |
1858 |
for line in $contents; { |
1859 |
case "$line" in |
1860 |
@name\ *) |
1861 |
pkgname="${line#@name }" |
1862 |
;; |
1863 |
@pkgdep\ *) |
1864 |
depends=";${line#@pkgdep }${depends:+$IFS}$depends" |
1865 |
;; |
1866 |
@comment\ *) |
1867 |
line="${line#@comment }" |
1868 |
case "$line" in |
1869 |
DEPORIGIN:*) |
1870 |
depends="${line#*:}$depends" |
1871 |
;; |
1872 |
PKG_FORMAT_REVISION:*) |
1873 |
format="${line#*:}" |
1874 |
;; |
1875 |
ORIGIN:*) |
1876 |
origin="${line#*:}" |
1877 |
;; |
1878 |
esac |
1879 |
;; |
1880 |
@conflicts\ *) |
1881 |
conflicts="${conflicts:+$conflicts$IFS}${line#@conflicts }" |
1882 |
;; |
1883 |
esac |
1884 |
} |
1885 |
|
1886 |
if [ "$format" != "1.1" ]; then |
1887 |
error $ERR_PACKAGE_FORMAT "Unknown package format in <$1>, bailing out!" |
1888 |
fi |
1889 |
|
1890 |
return 0 |
1891 |
} |
1892 |
|
1893 |
# |
1894 |
# Starts a download manager that can be instructed through a queue. |
1895 |
# A process that wants to know what's going on with the download manager |
1896 |
# can simply read from the queue as well. |
1897 |
# |
1898 |
# The download manager keeps as many downloads running as there are |
1899 |
# PACKAGESITE_MIRRORS. Should a download fail, it is retried as soon |
1900 |
# as no untried downloads remain. Every download is only retried once. |
1901 |
# A download is never attempted from the master server. |
1902 |
# |
1903 |
# @param queueMessages |
1904 |
# The queue to create and read from. |
1905 |
# @param packagerepos |
1906 |
# The location of the remote package repository (derived from |
1907 |
# PACKAGESITE). If this is identical with the local repository, |
1908 |
# the download manager will not be started. |
1909 |
# @param pNoActions |
1910 |
# If set, the download manager will not be started. |
1911 |
# |
1912 |
downloadManager() { |
1913 |
# No actions mode, this includes no downloads. |
1914 |
test -n "$pNoActions" && return 0 |
1915 |
|
1916 |
# Packages are locally available, no downloads. |
1917 |
test "$PACKAGES" = "$packagerepos" && return 0 |
1918 |
|
1919 |
verbose "Start the download manager." |
1920 |
|
1921 |
# Initialize the queue. |
1922 |
rm "$queueMessages" 2> /dev/null |
1923 |
touch "$queueMessages" |
1924 |
|
1925 |
# |
1926 |
# The following block is forked away. |
1927 |
# Note that all variable assignments happen in a separate process |
1928 |
# and hence have no effect on the outside. |
1929 |
# |
1930 |
( |
1931 |
# Remove the queue when exiting and get rid of pending jobs. |
1932 |
trap " |
1933 |
kill \$(jobs -ls) > /dev/null 2>&1 |
1934 |
rm '$queueMessages' 2> /dev/null |
1935 |
exit |
1936 |
" EXIT sigint sigterm |
1937 |
|
1938 |
# The jobs yet to be done. |
1939 |
jobs= |
1940 |
# The available mirrors. |
1941 |
mirrors="$PACKAGESITE_MIRRORS" |
1942 |
# The jobs that should be retried. |
1943 |
retry= |
1944 |
# The jobs that have been retried. |
1945 |
retried= |
1946 |
# The last line read from the socket. |
1947 |
line= |
1948 |
|
1949 |
# Keep on running as long as the father process is around. |
1950 |
# Note that this while loop has the message queue as stdin. |
1951 |
while kill -0 "$pid" 2> /dev/null; do |
1952 |
# Check for a message in the queue. |
1953 |
# There is nothing to be done, if there was no message, |
1954 |
# none the less it times out to allow the terminal |
1955 |
# to catch signals. |
1956 |
read -t 2 line |
1957 |
# Process messages. |
1958 |
case "$line" in |
1959 |
finished:*) |
1960 |
# A download has been finished. |
1961 |
# Add the mirror that was used to |
1962 |
# the list of available mirrors. |
1963 |
mirror="${line#finished:}" |
1964 |
mirror="${mirror%;*}" |
1965 |
mirrors="${mirrors:+$mirrors$IFS}$mirror" |
1966 |
;; |
1967 |
retry:*) |
1968 |
# A download was not finished |
1969 |
# successfuly. |
1970 |
mirror="${line#retry:}" |
1971 |
job="${mirror##*;}" |
1972 |
mirror="${mirror%;*}" |
1973 |
if echo "$retried" | grep -qFx "$job"; then |
1974 |
# If this package has already |
1975 |
# had a retry, mark it as |
1976 |
# finished to hand it over |
1977 |
# to the package validation |
1978 |
# that can fetch from the |
1979 |
# master server. |
1980 |
downloadManagerMsgFinished "$mirror" "$job" |
1981 |
else |
1982 |
# The first retry request. |
1983 |
# Free the mirror and list |
1984 |
# the package for retry. |
1985 |
mirrors="${mirrors:+$mirrors$IFS}$mirror" |
1986 |
retry="${retry:+$retry$IFS}$job" |
1987 |
fi |
1988 |
;; |
1989 |
request:*) |
1990 |
# Append requested downloads to the |
1991 |
# list of available jobs. |
1992 |
jobs="${jobs:+$jobs$IFS}${line#request:}" |
1993 |
;; |
1994 |
exit) |
1995 |
# The download manager has been told |
1996 |
# to terminate. |
1997 |
break |
1998 |
;; |
1999 |
esac |
2000 |
# Delete the line, so it cannot be read again in the |
2001 |
# next iteration, if reading from the queue has |
2002 |
# timed out. |
2003 |
line= |
2004 |
|
2005 |
# If any mirrors are available and there are jobs |
2006 |
# in the queue, now is the time to dispatch them. |
2007 |
while [ -n "$jobs" -a -n "$mirrors" ]; do |
2008 |
mirror="${mirrors%%$IFS*}" |
2009 |
mirrors="${mirrors#$mirror}" |
2010 |
mirrors="${mirrors#$IFS}" |
2011 |
job="${jobs%%$IFS*}" |
2012 |
jobs="${jobs#$job}" |
2013 |
jobs="${jobs#$IFS}" |
2014 |
downloadManagerFetch "$mirror" "$job" & |
2015 |
done |
2016 |
|
2017 |
# If we have run out of jobs, give the retry stuff. |
2018 |
# a try. |
2019 |
while [ -n "$retry" -a -n "$mirrors" ]; do |
2020 |
mirror="${mirrors%%$IFS*}" |
2021 |
mirrors="${mirrors#$mirror}" |
2022 |
mirrors="${mirrors#$IFS}" |
2023 |
job="${retry%%$IFS*}" |
2024 |
retry="${retry#$job}" |
2025 |
retry="${retry#$IFS}" |
2026 |
# Remember that this job has been retried. |
2027 |
retried="${retried:+$retried$IFS}$job" |
2028 |
downloadManagerFetch "$mirror" "$job" & |
2029 |
done |
2030 |
done < "$queueMessages" |
2031 |
) & |
2032 |
} |
2033 |
|
2034 |
# |
2035 |
# This is forked off by the download manager to download a package from |
2036 |
# a mirror. |
2037 |
# If the package is already present a download is not attempted. |
2038 |
# |
2039 |
# @param 1 |
2040 |
# The mirror to download from. |
2041 |
# @param 2 |
2042 |
# The name of the package to download. |
2043 |
# |
2044 |
downloadManagerFetch() { |
2045 |
# Get rid of pending jobs. |
2046 |
trap " |
2047 |
kill \$(jobs -ls) > /dev/null 2>&1 |
2048 |
exit |
2049 |
" EXIT sigint sigterm |
2050 |
|
2051 |
# Only do something if the package is not present. |
2052 |
if ! [ -e "$PACKAGES/All/$2.tbz" ]; then |
2053 |
# Create the download location. |
2054 |
mkdir -p "$PACKAGES/All" 2> /dev/null |
2055 |
|
2056 |
# Attempt download from mirror. |
2057 |
# This is forked off, to allow the shell to catch signals. |
2058 |
fetch -qmo "$PACKAGES/All/$2.tbz" "${1%/*?}/All/$2.tbz" > /dev/null 2>&1 & |
2059 |
if ! wait $!; then |
2060 |
# Release the mirror and mark the package for a retry. |
2061 |
downloadManagerMsgRetry "$1" "$2" |
2062 |
return 0 |
2063 |
fi |
2064 |
fi |
2065 |
|
2066 |
# Release the mirror and mark package finished. |
2067 |
downloadManagerMsgFinished "$1" "$2" |
2068 |
} |
2069 |
|
2070 |
# |
2071 |
# Tells the download manager, that a download was unsuccessful. |
2072 |
# |
2073 |
# @param 1 |
2074 |
# The mirror that was used. |
2075 |
# @param 2 |
2076 |
# The name of the package that was not downloaded. |
2077 |
# @param queueMessages |
2078 |
# The message queue to the download manager. |
2079 |
# |
2080 |
downloadManagerMsgRetry() { |
2081 |
# Do not send anything without a queue. |
2082 |
test ! -e "$queueMessages" && return 0 |
2083 |
|
2084 |
lockf -k "$queueMessages" sh -c "echo 'retry:$1;$2' >> '$queueMessages'" |
2085 |
} |
2086 |
|
2087 |
# |
2088 |
# Tells the download manager, that a download has been finished. |
2089 |
# |
2090 |
# @param 1 |
2091 |
# The mirror that was used. |
2092 |
# @param 2 |
2093 |
# The name of the downloaded package. |
2094 |
# @param queueMessages |
2095 |
# The message queue to the download manager. |
2096 |
# |
2097 |
downloadManagerMsgFinished() { |
2098 |
# Do not send anything without a queue. |
2099 |
test ! -e "$queueMessages" && return 0 |
2100 |
|
2101 |
lockf -k "$queueMessages" sh -c "echo 'finished:$1;$2' >> '$queueMessages'" |
2102 |
} |
2103 |
|
2104 |
# |
2105 |
# Requests the download of packages from the download manager. |
2106 |
# |
2107 |
# @param 1 |
2108 |
# A list of packages for download. |
2109 |
# @param queueMessages |
2110 |
# The message queue to the download manager. |
2111 |
# |
2112 |
downloadManagerMsgRequest() { |
2113 |
# Do not send anything without a queue. |
2114 |
test ! -e "$queueMessages" && return 0 |
2115 |
|
2116 |
local request |
2117 |
for request in $1; { |
2118 |
lockf -k "$queueMessages" sh -c "echo 'request:${request#*;}' >> '$queueMessages'" |
2119 |
} |
2120 |
} |
2121 |
|
2122 |
# |
2123 |
# Instructs the download manager to terminate. |
2124 |
# |
2125 |
# @param queueMessages |
2126 |
# The message queue to the download manager. |
2127 |
# |
2128 |
downloadManagerMsgExit() { |
2129 |
# Do not send anything without a queue. |
2130 |
test ! -e "$queueMessages" && return 0 |
2131 |
|
2132 |
lockf -k "$queueMessages" sh -c "echo 'exit' >> '$queueMessages'" |
2133 |
} |
2134 |
|
2135 |
# |
2136 |
# Validates a single package. Validation means it checks whether a package |
2137 |
# is a complete tar archive. Damaged or missing packages will be (re)downloaded |
2138 |
# from the master server (the one named by PACKAGESITE). |
2139 |
# If the package is a valid tar archive the +CONTENTS file will be checked, |
2140 |
# as well. |
2141 |
# |
2142 |
# @param 1 |
2143 |
# The name of the package to validate. |
2144 |
# @param packagerepos |
2145 |
# The location of the remote package collection. This is derived from |
2146 |
# PACKAGESITE. |
2147 |
# @param pending |
2148 |
# The list of pending packages. |
2149 |
# @return |
2150 |
# Return 0 on success. |
2151 |
# |
2152 |
validatePackage() { |
2153 |
local package |
2154 |
package="$1.tbz" |
2155 |
|
2156 |
# Check whether the package is intact and present. |
2157 |
if ! tar -tf "$PACKAGES/All/$package" > /dev/null 2>&1; then |
2158 |
# If the package repository and the local package collection |
2159 |
# are identical, there's no chance to get the package if it's |
2160 |
# not already there. |
2161 |
if [ "$PACKAGES" = "$packagerepos" ]; then |
2162 |
error $ERR_FETCH "The package <$package> is not present." |
2163 |
fi |
2164 |
|
2165 |
# Clean up whatever crap is there. |
2166 |
rm "$PACKAGES/All/$package" 2> /dev/null |
2167 |
|
2168 |
# Try to get the package from the master server. |
2169 |
fetch -mo "$PACKAGES/All/$package" "$packagerepos/All/$package" |
2170 |
|
2171 |
# Check whether the package is present. |
2172 |
if ! [ -e "$PACKAGES/All/$package" ]; then |
2173 |
error $ERR_FETCH "The package <$package> could not be fetched." |
2174 |
fi |
2175 |
|
2176 |
# Check whether the package is a valid tar archive. |
2177 |
if ! tar -tf "$PACKAGES/All/$package" > /dev/null 2>&1; then |
2178 |
error $ERR_FETCH "The package <$package> could not be read." |
2179 |
fi |
2180 |
fi |
2181 |
|
2182 |
# Check whether we can read the package +CONTENTS format. |
2183 |
readContents "$PACKAGES/All/$package" |
2184 |
|
2185 |
# Remove this package from the list of pending packages. |
2186 |
pending="$(echo "$pending" | grep -vFx "$1")" |
2187 |
|
2188 |
# The package is present and intact. |
2189 |
return 0 |
2190 |
} |
2191 |
|
2192 |
# |
2193 |
# Let's get it on! The declarative part is finally over. |
2194 |
# |
2195 |
|
2196 |
# Ignore some signals that should not occur. |
2197 |
trap 'warn "Discard signal SIGHUP."' sighup |
2198 |
trap 'warn "Discard signal SIGUSR1."' sigusr1 |
2199 |
trap 'warn "Discard signal SIGUSR2."' sigusr2 |
2200 |
|
2201 |
# |
2202 |
# Parse command line parameters. |
2203 |
# |
2204 |
readParams "$@" |
2205 |
|
2206 |
# Make sure the index is available for the following operations. |
2207 |
getIndex |
2208 |
|
2209 |
# |
2210 |
# Populate the list of packages out of sync with the index. |
2211 |
# |
2212 |
pkgAll |
2213 |
|
2214 |
# |
2215 |
# Perform dependency checking. |
2216 |
# |
2217 |
pkgDependencies |
2218 |
|
2219 |
# |
2220 |
# Sort packages by their dependencies. |
2221 |
# |
2222 |
pkgSort |
2223 |
|
2224 |
# |
2225 |
# Display tasks. |
2226 |
# |
2227 |
pkgList |
2228 |
|
2229 |
# |
2230 |
# Download packages. |
2231 |
# |
2232 |
pkgDownload |
2233 |
|
2234 |
# |
2235 |
# Upgrade packages. |
2236 |
# |
2237 |
pkgUpgrade |
2238 |
|
2239 |
exit 0 |