#!/bin/bash # slackware-upgrade-system - full upgrade of a Slackware installation # Copyright (C) 2019 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 # by the Free Software Foundation; either version 3, or (at your option) # any later version. # # Slackware-upgrade-system is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with slackware-upgrade-system. If not, see # . set -e # Slackware root directory rooturl= # Log file name logfile= # 'y', if dry-run mode is requested dry_run= # If 'y', don't ask anything, assume "y" as answer to all quieries assume_y= # Output verbosity: '' - normal, 'v' - verbose, 'q' - quiet verbosity= # If 'y', install all packages install_all= # Names of the series that should be installed in addition to already installed # packages. Ignored, if install_all is set. install_series= # Names of additional packages. install_packages= # Name of the keep-list file. keep_file= # Internal variables remote= tempdir=${TMP:-/tmp}/slackupg.$$ installed_list=$tempdir/installed.list avail_index=$tempdir/avail.index avail_list=$tempdir/avail.list series_names="a ap d e f k l n t tcl x xap xfce y " function tempdir_create() { u=$(umask) umask 077 mkdir $tempdir umask $u } function tempdir_remove() { rm -rf $tempdir } function usage() { cat <&2 "$0: $*" if [ -n "$logfile" ]; then echo >$logfile "$0: $*" fi } function abend { error "$@" tempdir_remove exit 1 } function package_file_name() { pkg=$(awk -vname=$1 '$1==name { print $2 }' $avail_index) if [ -z "$pkg" ]; then error "package $name not found in index (should not happen!)" else echo $pkg fi } function package_name_md5sum() { pkg=$(awk -vname=$1 '$1==name { print $3 }' $avail_index) } function check_package_md5sum() { if [ -n "$checksums" ]; then awk -vcname="$1" -vdname="$2" \ '$2==cname { print $1 " " dname }' $checksums | \ md5sum --status --check fi } function all_package_names() { grep -v '.*/kde[^/]*/' $avail_index | cut -d ' ' -f 1 } function series_package_names() { grep '.*/'"$1/" $avail_index | cut -d ' ' -f 1 } function catfile() { echo $rooturl/${1#./} } function download_curl { curl $CURL_OPTIONS -L -sS -o$1 $2 } function download_wget { if ! wget $WGET_OPTIONS -nv -o wget.log -O$1 $2; then grep -i "failed\|error" wget.log /bin/false fi } function dnfunc_init() { if [ -z "$dnfunc" ]; then if curl --version >/dev/null 2>&1; then dnfunc=download_curl elif wget --version >/dev/null 2>&1; then dnfunc=download_wget else abend "neither curl nor wget is installed" fi fi } function download() { local name=$(basename $1) local url=$(catfile $1) if $dnfunc $name $url; then echo $name fi } function getfile() { local name=$(if [ -n "$remote" ]; then download $1 else catfile $1 fi) if [ -n "$2" ]; then ascname=$(if [ -n "$remote" ]; then download $1.asc else catfile $1.asc fi) if [ -n "$ascname" ] \ && ${GPG:-gpg} --verify $ascname $name 2>/dev/null; then : else error "gpg verification failed for $name" return fi fi if [ -n "$checksums" ] && ! check_package_md5sum $1 $name; then error "ERROR: $1: checksum failed" name= fi echo $name } function dropfile() { if [ -n "$remote" ]; then rm $1 fi } function upgrade_package() { local name=$1 shift if [ -n "$dry_run" ]; then echo "upgradepkg $@ $name" else file=$(getfile $name) if [ -n "$file" ]; then if [ "$verbosity" = 'v' ]; then upgradepkg "$@" $file | tee -a $logfile | sed -n -e '/^| Upgrading/s/^| //p' else upgradepkg "$@" $file >> $logfile fi dropfile $file fi fi } function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1" } while getopts "ahknp:qs:vy" OPTION do case $OPTION in a) install_all=y;; h) usage exit 0;; k) keep_file=$OPTARG;; n) dry_run=y;; p) install_packages="$install_packages $OPTARG";; q) verbosity=q;; v) verbosity=v;; s) install_series="$install_series $OPTARG";; y) assume_y=y;; *) usage >&2 exit 1 esac done shift $(($OPTIND - 1)) case $# in 0) ;; 1) rooturl=$1;; *) error "unexpected arguments" usage >&2 exit 1 esac if [ $(id -u) != "0" ]; then abend "must be root" fi # Sanity check if [ ! -s /etc/os-release ]; then abend "/etc/os-release doesn't exist" fi . /etc/os-release if [ "$ID" != "slackware" ]; then abend "this doesn't seem to be a Slackware installation" fi case "$(uname -m)" in i?86) ARCH=;; x86_64) ARCH=64;; *) abend "architecture $(uname -m) is not yet supported" esac tempdir_create cd $tempdir mirrors_url=https://mirrors.slackware.com/slackware if [ -z "$rooturl" ]; then dnfunc_init if ! $dnfunc index.html $mirrors_url; then abend "exiting" exit 1 fi version_rx=$(echo $VERSION | sed -e 's/\./\\./g') new_version=$(cat index.html |\ tr '<' '\n' |\ sed -n -r \ -e 's/.*^a href="slackware'$ARCH'-([[:digit:].]+)\/?".*/\1/p'|\ sed -n -e "/$version_rx/{" -en -ep -e '}' ) if [ -n "$new_version" ]; then rooturl="$mirrors_url/slackware$ARCH-$new_version" error "info: using $rooturl as distribution top-level URL" else abend "can't find distribution newer than $VERSION; please supply URL if you have any" fi fi # Check if rooturl is local or remote case $rooturl in /*) unset remote ;; http://*|https://*|ftp://*|ftps://*) dnfunc_init remote=1 ;; *) abend "root directory must be absolute file name or URL" esac if [ -z "$remote" ]; then # Check if rooturl exists and contains the necessary files and directories if [ ! -d $rooturl ]; then abend "$rooturl does not exist" fi fi # Check if rooturl contains all we need error "info: verifying distribution..." # # Download CHECKSUMS.md5. So far it is the only file that is gpg-checked. # For the rest we rely on MD5 sums. checksums=$(getfile CHECKSUMS.md5 gpg) if [ -z "$checksums" ]; then abend "CHECKSUMS.md5 not found in $rooturl" fi announce=$(tail +13 $checksums | \ sed -n -r\ -e 's/^[0-9a-fA-F]+[[:space:]]+(\.\/ANNOUNCE\.[[:digit:]_]+)$/\1/p') if [ -z "$announce" ]; then abend "ANNOUNCE not found in $rooturl" fi file=$(getfile $announce) if [ -z "$file" ]; then abend "file $announce not found in $rooturl" fi newversion=$(echo "$announce" | sed -e 's/\.\/ANNOUNCE\.//' -e 's/_/./g') if [ -z "$newversion" ]; then abend "cannot determine new version" fi # Create list and index of available files tail +13 $checksums | \ sed -r \ -n \ -e 's/^[0-9a-fA-F]+[[:space:]]+(\.\/slackware(64)?\/.*\/(.*)-[^-]+-(i386|x86(_64)?|arm|noarch|fw)-[[:digit:]]+(_.*)?\.t.z)$/\3 \1/p' | \ tee $avail_index | awk '{print $1}' | sort > $avail_list # Initialize log file name logstem=/var/log/slackware-upgrade-system-$VERSION-$newversion logfile=$logstem.log # Check if pkgdir exists and contains the necessary files and directories for series in $series_names do n=$(sed -n -r -e 's/^[^[:space:]]+[[:space:]]+(\.\/slackware(64)?\/'$series'\/.*\.t.z)$/\1/p' $avail_index | head -1) if [ -z "$n" ]; then abend "no files in series $series" fi file=$(getfile $n) if [ -z "$file" ]; then abend "exiting" fi dropfile $file done # Build a list of installed packages ls /var/log/packages |\ sed -r -e 's/-[^-]+-(i386|x86(_64)?|arm|noarch|fw)-[[:digit:]]+(_.*)?//' |\ sort > $installed_list # Build a list of packages to install (if [ -n "$install_all" ]; then all_package_names else comm -1 -2 $installed_list $avail_list fi for s in $install_series do series_package_names $s done if [ -n "$install_packages" ]; then echo $install_packages fi) | sort -u > candidates # Build a list of packages that should be removed after install remove_list=$logstem.removed comm -2 -3 $installed_list candidates > $remove_list.$$ if [ -s "$keep_file" ]; then grep -v '^#' $keep_file | \ tr -s '\n' | sort | comm -2 -3 $remove_list.$$ - > $remove_list rm $remove_list.$$ else mv $remove_list.$$ $remove_list fi # Disable interrupts during critical section trap '' HUP INT QUIT ABRT if [ -s $remove_list -a "$verbosity" != 'q' ]; then (cat <> $logfile fi echo "$0: see $remove_list for the list of packages that have been removed" fi echo "$0: upgrade finished; see $logfile for details" tempdir_remove conffiles=$logstem.conffiles find /etc /usr/lib*/ /usr/share/vim -name "*.new" 2>/dev/null | \ sort > $conffiles if [ ! -s "$conffiles" ]; then rm $conffiles fi if [ "$verbosity" != 'q' ]; then echo "IMPORTANT!" if [ -s "$conffiles" ]; then cat <