diff options
-rw-r--r-- | slackupgrade | 288 | ||||
-rw-r--r-- | slackupgrade.8 | 158 |
2 files changed, 353 insertions, 93 deletions
diff --git a/slackupgrade b/slackupgrade index 662b325..ff4d46f 100644 --- a/slackupgrade +++ b/slackupgrade @@ -2,6 +2,6 @@ # slackupgrade - full upgrade of a Slackware installation -# Copyright (C) 2019 Sergey Poznyakoff. +# Copyright (C) 2019-2020 Sergey Poznyakoff. # # Slackware-upgrade-system is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as published +# modify it under the terms of the GNU General Public License as published # by the Free Software Foundation; either version 3, or (at your option) @@ -22,2 +22,3 @@ set -e : ${SLACKUPGRADE_CONFDIR:=/etc/slackupgrade} +: ${SLACKUPGRADE_PKGDIR:=/var/slackupgrade} # Slackware root directory @@ -43,2 +44,16 @@ keep_file= max_backup=4 +# Display progress bar when downloading +progressbar=1 +# Operating mode. Possible values: +# INCR Incremental mode. Archive for each package is downloaded prior +# to its nstallation and removed immediately afterwards. This +# ensures minimal disk space requirements. +# SAFE Safe mode. All archives are first downloaded to +# SLACKUPGRADE_PKGDIR and then installed. Each archive is removed +# immediately after installing from it. This requires some 2.5G +# of disk space at the beginning, which will be freed by the end +# of upgrade. +# AUTO Select incremental mode if there is enough disk space. If not, +# ask the user if it is OK to proceed in incremental mode. +opmode=AUTO @@ -47,2 +62,3 @@ remote= tempdir=${TMP:-/tmp}/slackupg.$$ +strip_series=0 installed_list=$tempdir/installed.list @@ -134,2 +150,4 @@ function error() { +# Abnormal termination: print error message, remove temporary directory +# and terminate with status 1. function abend() { @@ -145,10 +163,8 @@ function package_file_name() { else - echo $pkg - fi -} - -function package_name_md5sum() { - pkg=$(awk -vname=$1 '$1==name { print $3 }' $avail_index) + echo $pkg + fi } +# check_package_md5sum PKG ARCHIVE +# Verifies the MD5 sum of the ARCHIVE file for Slackware package PKG function check_package_md5sum() { @@ -161,2 +177,4 @@ function check_package_md5sum() { +# all_package_names +# Lists all packages from the Slackware distribution. function all_package_names() { @@ -165,2 +183,4 @@ function all_package_names() { +# series_package_names S +# Lists the names of packages in Slackware series S. function series_package_names() { @@ -173,2 +193,4 @@ function catfile() { +# download_curl FILE URL +# Downloads file from URL to FILE using curl. function download_curl { @@ -177,2 +199,4 @@ function download_curl { +# download_wget LOCAL URL +# Downloads file from URL to FILE using wget. function download_wget { @@ -184,2 +208,4 @@ function download_wget { +# dnfunc_init +# Initializes the downloader function to wget or curl. function dnfunc_init() { @@ -196,7 +222,10 @@ function dnfunc_init() { +# download FILE +# Downloads package FILE from the remote server. On success, returns the +# full pathname of the downloaded copy. function download() { - local name=$(basename $1) + local name=$SLACKUPGRADE_PKGDIR/$(basename $1) local url=$(catfile $1) if $dnfunc $name $url; then - echo $name + echo $name fi @@ -204,22 +233,30 @@ function download() { +# getfile PKG [GPG] +# Retrieves the package archive file for the package PKG. If second argument +# is non-empty, verifies the GPG signature of the file. +# If checksums file is available, verifies also the MD5 checksum of the file. +# +# Returns full pathname of the retrieved file. function getfile() { local name=$(if [ -n "$remote" ]; then - download $1 - else - catfile $1 - fi) - + download $1 + elif [ $strip_series -eq 1 ]; then + catfile $(basename $1) + else + catfile $1 + fi) + if [ -n "$2" ]; then ascname=$(if [ -n "$remote" ]; then - download $1.asc - else - catfile $1.asc - fi) + download $1.asc + else + catfile $1.asc + fi) if [ -n "$ascname" ] \ && ${GPG:-gpg} --verify $ascname $name 2>/dev/null; then - : - else + : + else error "gpg verification failed for $name" return - fi + fi fi @@ -233,8 +270,13 @@ function getfile() { +# dropfile FILE +# Removes FILE, if it has been downloaded from the remote repository, function dropfile() { - if [ -n "$remote" ]; then + if [ -n "$remote" ] || [ $strip_series -eq 1 ]; then rm $1 fi -} +} +# upgrade_package [OPTIONS] FILE +# Runs upgradepkg with the supplied arguments and captures its output to +# the log file. In verbose mode, filters parts of it to the stdout. function upgrade_local() { @@ -251,2 +293,4 @@ function upgrade_local() { +# upgrade_package NAME [OPTIONS] +# Upgrades the package NAME. OPTIONS will be passed to upgradepkg verbatim. function upgrade_package() { @@ -265,2 +309,4 @@ function upgrade_package() { +# version_gt A B +# Returns true if version A is greater than B. function version_gt() { @@ -292,10 +338,10 @@ function backup() { do - if [ -n "$max_backup" ] && [ $n -ge $max_backup ]; then - $p rm $dir/${stem}~$n - else - $p mv $dir/${stem}~$n $dir/${stem}~$((n + 1)) - fi - done + if [ -n "$max_backup" ] && [ $n -ge $max_backup ]; then + $p rm $dir/${stem}~$n + else + $p mv $dir/${stem}~$n $dir/${stem}~$((n + 1)) + fi + done $p mv "$1~" "$1~1" - fi + fi $p mv "$1" "$1~" @@ -304,2 +350,54 @@ function backup() { +# disk_avail_size DIR +# Returns the available disk size (in kilobytes) on the device where +# DIR is located +function disk_avail_size() { + df -k --output=target,avail | \ + sed 1d | \ + awk -vdir=$1 \ + 'BEGIN { dirlen=length(dir) } + { len = length($1) + if (len <= dirlen && substr(dir, 1, len) == $1) { + if (mp_len < len) { + mp_len = len + mp_avail = $2 + } + } + } + END { print mp_avail }' +} + +# Downloads all selected packages to the spool directory. +function download_all() { + info "downloading packages" + if [ ${COLUMNS:-0} -lt 10 ]; then + progressbar=0 + fi + if [ $progressbar -eq 1 ]; then + total=$(wc -l candidates | cut -d ' ' -f 1) + width=$(( $COLUMNS - 7 )) + fi + i=0 + cat candidates | + while read pkg + do + if [ $progressbar -eq 1 ]; then + n=$(( $i * $width / $total )) + s=$(printf "%${n}s" ' '|sed 's/./=/g') + printf "\r% 3d%% %s>" $(( $i * 100 / $total)) $s + fi + if [ -z "$dry_run" ]; then + file=$(getfile $(package_file_name $pkg)) + if [ -z "$file" ]; then + abend "failed to download $pkg" + fi + fi + i=$(( $i + 1 )) + done || exit $? + if [ $progressbar -eq 1 ]; then + s=$(printf "%${width}s" ' '|sed 's/./=/g') + printf "\r% 3d%% %s|\n" 100 $s + fi +} + # ########## @@ -308,5 +406,7 @@ function backup() { -while getopts "ahknp:qs:vy" OPTION +while getopts "ahIknp:qSs:vy" OPTION do case $OPTION in + I) opmode=INCR;; + S) opmode=SAFE;; a) install_all=y;; @@ -356,2 +456,6 @@ esac +if [ ! -d $SLACKUPGRADE_PKGDIR ]; then + mkdir -p $SLACKUPGRADE_PKGDIR || abend "can't create $SLACKUPGRADE_PKGDIR" +fi + tempdir_create @@ -378,3 +482,3 @@ if [ -z "$rooturl" ]; then fi -fi +fi @@ -396,2 +500,4 @@ if [ -z "$remote" ]; then fi + # Safe mode is meaningless with local repository + opmode=INCR fi @@ -411,6 +517,6 @@ announce=$(tail +13 $checksums | \ sed -n -r\ - -e 's/^[0-9a-fA-F]+[[:space:]]+(\.\/ANNOUNCE\.[[:digit:]_]+)$/\1/p') + -e 's/^[0-9a-fA-F]+[[:space:]]+(\.\/ANNOUNCE\.[[:digit:]_]+)$/\1/p') if [ -z "$announce" ]; then abend "ANNOUNCE not found in $rooturl" -fi +fi file=$(getfile $announce) @@ -418,3 +524,3 @@ if [ -z "$file" ]; then abend "file $announce not found in $rooturl" -fi +fi @@ -453,3 +559,3 @@ do dropfile $file -done +done @@ -463,5 +569,5 @@ ls /var/log/packages |\ all_package_names -else +else comm -1 -2 $installed_list $avail_list -fi +fi for s in $install_series @@ -479,3 +585,3 @@ if [ -s "$keep_file" ]; then grep -v '^#' $keep_file | \ - tr -s '\n' | sort | comm -2 -3 remove.list.$$ - > remove.list + tr -s '\n' | sort -u | comm -2 -3 remove.list.$$ - > remove.list rm remove.list.$$ @@ -490,7 +596,7 @@ if [ -f "$SLACKUPGRADE_CONFDIR/$VERSION-$newversion.repl" ]; then awk '{ sub(/#.*$/,""); sub(/[[:space:]]+$/,"") } - /\\$/ { sub(/\\$/,""); p = p $0; next } - NF == 0 { if (p) print p; p = ""; next } - { print p $0; p="" }' \ + /\\$/ { sub(/\\$/,""); p = p $0; next } + NF == 0 { if (p) print p; p = ""; next } + { print p $0; p="" }' \ $SLACKUPGRADE_CONFDIR/$VERSION-$newversion.repl | \ - sort +0 -1 | \ + sort +0 -1 | \ tee rename.$$ | \ @@ -503,5 +609,5 @@ if [ -f "$SLACKUPGRADE_CONFDIR/$VERSION-$newversion.repl" ]; then awk '{print $1}' rename.$$ | \ - comm -13 - remove.list > $remove_report + comm -13 - remove.list > $remove_report # Clean up - rm rename.$$ + rm rename.$$ else @@ -521,3 +627,3 @@ in the file $remove_report: EOF -cat $remove_report) | ${PAGER:-more} + cat $remove_report) | ${PAGER:-more} fi @@ -538,2 +644,85 @@ fi +if [ $opmode != INCR ]; then + # Automatic mode selection. Compute total compressed size of packages + # to be installed and compare it with available disk space on the device + # hosting SLACKUPGRADE_PKGDIR. Select safe mode if there is enough space, + # otherwise ask if the user wishes to continue in incremental mode. + packages=$(download PACKAGES.TXT) + if [ -z "$packages" ]; then + abend "PACKAGES.TXT not found in $rooturl" + fi + download_size=$(sed -n -r \ + -e '/PACKAGE NAME:[[:space:]]*/{' \ + -e 's///' \ + -e 's/^(.*)-[^-]+-(i386|x86(_64)?|arm|noarch|fw)-[[:digit:]]+(_.*)?\.t.z$/\1 &/' \ + -e h \ + -e '}' \ + -e '/PACKAGE SIZE \(compressed\):[[:space:]]*/{' \ + -e 's///' \ + -e H \ + -e x \ + -e 's/\n/ /' \ + -e p \ + -e '}' \ + $packages | \ + sort +0 -1 | \ + join candidates - | \ + awk '{ if ($4 == "M") s += $3*1024 + else if ($4 == "G") s += $3*1024*1024 + else s += $3 } + END { print s }') + avail_size=$(disk_avail_size $SLACKUPGRADE_PKGDIR) + if [ $avail_size -lt $download_size ]; then + error "not enough free space in $SLACKUPGRADE_PKGDIR" + error "needed ${download_size}K, but only ${avail_size}K available" + if [ $opmode = INCR ]; then + abend "terminating" + fi + + cat >&2 <<EOT + +Safe mode is the default for slackupgrade. In this mode, the program would +first download all packages from the remote site, verify them and then +proceed to actual upgrade. This ensures that even if something goes wrong +during the upgrade, you have all tarballs at hand and can resume from +where you left after fixing the problem. + +This mode cannot be used due to the disk space shortage. Slackupgrade can +nevertheless continue in incremental download mode. In this mode, it will +download and install packages one by one. This takes more time and is less +error prone. If you choose this mode, type 'Y' at the prompt below. Other- +wise, type 'N' to terminate the program, and follow the instructions you +will get. + +EOT + + if getyn n "Continue in incremental download mode"; then + opmode=INCR + else + cat >&2 <<EOT + +Please make sure the slackupgrade spool directory can accomodate at least +${download_size}K of data. To do so, either free sufficient disk space on +the device where $SLACKUPGRADE_PKGDIR resides, or set the SLACKUPGRADE_PKGDIR +environment variable to the full pathname of the new spool directory, + +EOT + error "Terminating." + exit 0 + fi + else + # Download all packages + download_all + # Switch to the local installation mode + rooturl=$SLACKUPGRADE_PKGDIR + opmode=INCR + # Instruct getfile to strip off the series directory from the + # package names. + strip_series=1 + # Disable remote indicator and checksum verification (all archives + # have already been verified during the download). + unset remote checksums + fi +fi + info "starting upgrade of Slackware $VERSION to $newversion${dry_run:+ (DRY RUN MODE)}" @@ -546,3 +735,3 @@ if [ -n "$remote" ]; then pkg_names="$pkg_names aaa_elflibs wget" -fi +fi pkg_re='^('$(echo "$pkg_names" | sed -r -e 's/ +/|/g')')$' @@ -567,3 +756,3 @@ if [ -n "$remote" ]; then dnfunc=download_wget -fi +fi @@ -632,2 +821 @@ EOF fi - diff --git a/slackupgrade.8 b/slackupgrade.8 index 8d88b58..d58b0e7 100644 --- a/slackupgrade.8 +++ b/slackupgrade.8 @@ -1,3 +1,3 @@ .\" This file is part of slackupgrade -.\" Copyright (C) 2019 Sergey Poznyakoff. +.\" Copyright (C) 2019-2020 Sergey Poznyakoff. .\" @@ -16,5 +16,5 @@ .\" <http://www.gnu.org/licenses/>. -.TH SLACKUPGRADE 8 "September 23, 2019" "SLACKUPGRADE" "System Manager's Manual" +.TH SLACKUPGRADE 8 "February 7, 2020" "SLACKUPGRADE" "System Manager's Manual" .SH NAME -slackwupgrade \- do a full upgrade of a Slackware installation +slackupgrade \- do a full upgrade of a Slackware installation .SH SYNOPSIS @@ -23,3 +23,3 @@ slackwupgrade \- do a full upgrade of a Slackware installation \fBslackupgrade\fR\ - [\fB\-anqvy\fR]\ + [\fB\-aInSqvy\fR]\ [\fB\-k \fIFILE\fR]\ @@ -50,3 +50,3 @@ first. Then, the program verifies that the signature is correct. For this to succeed, you must have the Slackware Linux Project -public key in your keyring. If you don't, run +public key in your GPG keyring. If you don't, run .PP @@ -58,14 +58,7 @@ When this initial check is passed, the program constructs two lists of packages: a list of currently installed packages and a list of -packages available in the distribution. When constructing the list of -available packages, known differences between Slackware releases are -taken into account. For example, consider upgrade from version 14.1 to -14.2. It is known that the \fIportmap\fR package from 14.1 is -replaced with the \fIrpcbind\fR in version 14.2. Consequently, if the -program sees that \fIportmap\fR is installed on the system, it will -include \fIrpcbind\fR to the list of installation candidates. -Information about package differences in various versions is kept in -\fIreplacement map files\fR. See the section \fBREPLACEMENT MAP\fR, -for a discussion of these files. +packages available in the distribution. The intersection of these two +is the list of \fIupgrade candidates\fR, i.e. packages that will be +upgraded. .PP -The difference between these two lists, is the set of installed +The difference between these lists is the set of installed packages that have no equivalent in the available package list. Those @@ -73,3 +66,3 @@ are \fIorphaned packages\fR, which were either removed from the Slackware distribution, or were installed from third-party sources. It is -unpredictable whether or not these will work on the newly upgraded +unpredictable whether these will work on the newly upgraded system, therefore they will be removed after a successful upgrade. @@ -84,28 +77,17 @@ the upgrade, you can re-install them, if necessary. .PP -After this step, the program will print the current Slackware version -and the version you are going to upgrade to, and will ask you to -confirm that you really want to upgrade. This is the right moment to -quit if you decide to modify program invocation in order to handle -orphaned packages. +Known differences between Slackware releases are taken into account +when building lists of upgrade and delete candidates. For example, +consider an upgrade from version 14.1 to 14.2. It is known that the +\fIportmap\fR package from 14.1 is replaced with the \fIrpcbind\fR in +version 14.2. Consequently, if the program sees that \fIportmap\fR is +installed on the system, it will include \fIrpcbind\fR to the list of +installation candidates. Information about package differences in +various versions is kept in \fIreplacement map files\fR. See the +section \fBREPLACEMENT MAP\fR, for a discussion of these files. .PP -If you do, type \fBno\fR. You have two options. First, if there are -any orphaned packages that you want to keep in place, create a -\fIkeep-list\fR file. This file should contain names of those -packages, each name on a separate line. When you restart the program -use the \fB\-k \fIFILE\fR\fR option to instruct it to use this file. -.PP -Secondly, if you are upgrading to the version for which there is no -replacement map, there can be replacement packages for some of the -orphaned ones. You can create a replacement map and save it to -the \fB/etc/slackupgrade\fR directory (see the section -\fBREPLACEMENT MAP\fR for details). If you do, please drop me a note -so that your changes become available for other users (see the -\fBBUGS\fR section, for contact information). You can also use the -\fB\-p\fR command line option to provide the names of replacement -packages from the command line. -.PP -By default, the program will upgrade only the packages actually -installed on your system. You can request to install additional -series from the distribution with the \fB\-s\fR option. E.g., it is -often a good idea to install all packages from series \fBl\fR, like that: +In addition to upgrading already installed packages you can request +the program to install additional series or individual packages +from the distribution with the \fB\-s\fR and \fB\-p\fR options. E.g., +it is often a good idea to install all packages from series \fBl\fR (ell), +like that: .PP @@ -119,2 +101,47 @@ series, excepting \fBkde*\fR, by running the command with the .PP +Once the package lists are ready, the program prints the current +Slackware version and the version it is going to upgrade to, and asks +you to confirm that you really want to upgrade. This is the right +moment to quit if you decide to modify program invocation in order to +handle orphaned packages. See the subsection +.B Handling of orphaned packages +for a discussion. +.PP +If you decide to continue and \fBslackupgrade\fR uses the remote +repository, it will select the upgrade mode to use. There are two +possibilities: +.TP +.I Incremental mode +In this mode, the archive for each package will be downloaded +immediately prior to its installation and removed afterwards. The +only exception are several basic packages that are needed for the +upgrade process and which will be downloaded and installed in the +first place. + +This mode requires little additional disk space, but it is somewhat +risky. If something happens to your internet connection during the +upgrade, you'll end up with partly upgraded (and consequently, +unstable) system. +.TP +.I Safe mode +In safe mode, \fBslackupgrade\fR first downloads all needed packages +from the remote repository and then proceeds to upgrade using local +disk copies. Downloaded files are removed when no longer needed, i.e. +immediately after the corresponding package has been installed. + +This mode requires around 2.5G of free disk space on the device which +hosts the \fBslackupgrade\fR spool directory (by default -- +.BR /var/slackupgrade ). +You can select another location by setting the +\fBSLACKUPGRADE_PKGDIR\fR environment variable to the full pathname +of the spool directory you wish to use. + +The advantage of the safe mode is that even if something goes wrong +during the upgrade, you have all the necessary stuff at hand to resume +upgrade from where you left off. +.PP +Safe mode is assumed if there is enough disk space. Otherwise, the +program will ask you whether you wish to continue in incremental mode +and will act accordingly. +.PP At the end of the run, the program prints additional instructions and @@ -133,2 +160,20 @@ files will be stored in file \fB/var/log/slackupgrade\-\fIOLD\fB\-\fINEW\fB.new\fR. +.SS Handling of orphaned packages +There are two possible ways to handle installed packages that have no +installation candidate in the new Slackware release. + +First, if you want to keep in place any of them, create a +\fIkeep-list\fR file. This file is a list of package names, each on a +separate line. When you start \fBslackupgrade\fR, use the +\fB\-k \fIFILE\fR\fR option to instruct it to use this file. +.PP +Secondly, if you are upgrading to the version for which there is no +replacement map, there can be replacement packages for some of the +orphaned ones. You can create a replacement map and save it to +the \fB/etc/slackupgrade\fR directory (see the section +\fBREPLACEMENT MAP\fR for details). If you do, please drop me a note +so that your changes become available for other users (see the +\fBBUGS\fR section, for contact information). You can also use the +\fB\-p\fR command line option to provide the names of replacement +packages from the command line. .SH OPTIONS @@ -141,2 +186,5 @@ Display a short help summary and exit. .TP +.B \-I +Force using incremental mode (see also \fB\-S\fR). +.TP \fB\-k \fIFILE\fR @@ -171,2 +219,5 @@ Quiet mode: suppress all messages, except error diagnostics. .TP +.B \-S +Force using safe mode. Abort if not enough disk space is available. +.TP \fB\-s \fISERIES\fR @@ -221,2 +272,8 @@ Replacement map for upgrades from version \fIOLD\fR to \fINEW\fR. .TP +.B /var/slackupgrade +Default spool directory. This is where all downloaded files are +stored. You can override the default location by setting the +.B SLACKUPGRADE_PKGDIR +environment variable. +.TP \fB/var/log/slackupgrade\-\fIOLD\fB\-\fINEW\fB.log\fR @@ -252,2 +309,17 @@ becomes \fBX~2\fR and so on. At most five backup copies are kept (from \fBX~\fR up to \fBX~4\fR). +.SH ENVIRONMENT +.TP +.B SLACKUPGRADE_CONFDIR +Location of the configuration directory. This is the place where +replacement maps are stored. The default is +.BR /etc/slackupgrade . +.TP +.B SLACKUPGRADE_PKGDIR +Location of the \fBslackupgrade\fR package spool directory. This is +the temporary storage for downloaded package tarballs. The default is +.BR /var/slackupgrade . +.TP +.B TMP +Location of the temporary directory. The default is +.BR /tmp . .SH "SEE ALSO" @@ -265,3 +337,3 @@ Report bugs to <gray@gnu.org>. .SH COPYRIGHT -Copyright \(co 2019 Sergey Poznyakoff +Copyright \(co 2019 \(em 2020 Sergey Poznyakoff .br |