diff options
-rw-r--r-- | LICENSE.txt | 20 | ||||
-rw-r--r-- | README.md | 107 | ||||
-rw-r--r-- | src/autoupdate | 186 | ||||
-rw-r--r-- | src/install-kernel | 22 | ||||
-rw-r--r-- | src/rc.update | 56 | ||||
-rwxr-xr-x | src/test/bin/chmod | 2 | ||||
-rwxr-xr-x | src/test/bin/chown | 2 | ||||
-rwxr-xr-x | src/test/bin/tar | 2 | ||||
-rw-r--r-- | src/test/test_autoupdate.sh | 129 |
9 files changed, 526 insertions, 0 deletions
diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f146b6f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright 2024 Slack Coder + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d98e78b --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# Slack Autoupdate + +Update your Slackware system automatically by integrating these customizable +bash scripts. + +Pending update information is saved into the 'info' and 'error' text files +under '/var/spool/slackware-autoupdate'. 'info.txt' should contain package +update information, and 'error.txt' contains any problems encountered while +attempting to source updates. + +## Support + +These scripts only currently support stable releases (sorry -current). + +## Installation and Configuration + +There are three components. the [cron script](./src/autoupdate) which downloads +packages, the [rc.update script](./src/rc.update) which installs the packages, and +the [install-kernel script](./src/install-kernel) which updates your computer after a +kernel update. + +### Cron script + +Your system downloads the packages using this script. + +Adjust the script using your favorite editor: + +``` +vim src/autoupdate +``` + +Then as root: + +``` +cp src/autoupdate /etc/cron.daily +chown root:root /etc/cron.daily/autoupdate +chmod +x /etc/cron.daily/autoupdate +``` + +### rc.update + +Your system updates your installed packages using this script. + +Adjust the script using your favorite editor: + +``` +vim src/rc.update +``` + +Then as root: + +``` +cp src/rc.update /etc/rc.d/ +chown root:root /etc/rc.d/rc.update +``` + +Then add this to /etc/rc.d/rc.local: + +``` +if [ -x /etc/rc.d/rc.update ]; then + /etc/rc.d/rc.update +fi +``` + +To enable installation on reboot: + +``` +chmod +x /etc/rc.d/rc.update +``` + +or to disable it again: + +``` +chmod -x /etc/rc.d/rc.update +``` + +### install-kernel + +This part depends on your system setup and will automatically update the kernel +into your system. You should modify it to suit your circumstance. + +The default script assumes you are using elilo and the kernel for your EFI is +installed into /efi/EFI/Slackware/vmlinuz. + +Adjust the script to your system using your favorite editor or vim: + +``` +vim install-kernel +``` + +Then as root: + +``` +cp install-kernel /usr/local/sbin +chown root:root /usr/local/sbin/install-kernel +chmod +x /usr/local/sbin/install-kernel +``` + +## Source + +### Testing + +You can test the project by running the shell files under the 'test' folder. + +``` +sh src/test/*.sh +``` diff --git a/src/autoupdate b/src/autoupdate new file mode 100644 index 0000000..04e432b --- /dev/null +++ b/src/autoupdate @@ -0,0 +1,186 @@ +#!/bin/bash +# +# Update your system automatically! +# +# Packages from different sources are downloaded, under sub-shells, into the +# PACKAGE_DIR where they can be installed on reboot. +# +# A failure encountered at any point should abort any pending package updates +# to install. +# +# To enable sbotools updates, make the /etc/sbotools/sbotools.conf file exist +# and configure PKG_DIR to point to the same path as STAGING_DIR. + +# Set to "yes" to send notification to stdout, causing cron to send an email. +NOTIFY="${NOTIFY:-no}" + +# Reboot after this period for install after a successful update. +# +# Set to empty to disable reboot or 'now' to reboot immediately. +export REBOOT_TIME="${REBOOT_TIME:-""}" + +# Packages are copied here on successful download. +export PACKAGE_DIR="${PACKAGE_DIR:-/var/spool/slack-autoupdate}" + +# Packages are temperarily stored here until success. +export STAGING_DIR="${STAGING_DIR:-/var/cache/slack-autoupdate/staging}" + +# Information of interest to the admin on success. +export UPDATE_INFO="${UPDATE_INFO:-$STAGING_DIR/info.txt}" + +# Information of interest to the admin on failure. +export UPDATE_ERROR="${UPDATE_ERROR:-$STAGING_DIR/error.txt}" + +# Slackware mirror containing packages. +export SLACKWARE_MIRROR="${SLACKWARE_MIRROR:-rsync://mirrors.kernel.org/slackware/slackware64-15.0}" + +# A local Slackware mirror with just enough information to support slackpkg. +# +# This step could be skipped if slackpkg supports downloading updates without +# installing them. +export LOCAL_MIRROR=${LOCAL_MIRROR:-"/var/cache/slack-autoupdate/mirror"} + +# Avoid concurrently running with another instance. +if [ "$(ls /var/lock/autoupdate.* 2>/dev/null)" ]; then + echo "Another instance of autoupdate is running. If this is not correct, you can remove /var/lock/autoupdate.* files and run autoupdate again." \ + >>"$PACKAGE_DIR/$(basename $UPDATE_ERROR)" + if [ "$NOTIFY" = "yes" ]; then + cat "$PACKAGE_DIR/$(basename $UPDATE_ERROR)" + fi + + exit 1 +fi + +touch /var/lock/autoupdate.$$ +trap "rm -f /var/lock/autoupdate.$$" EXIT + +# Keep it simple stupid, install pending updates first. +mkdir --parents "$PACKAGE_DIR" +if [ -n "$(find "$PACKAGE_DIR" -name "*.t*z")" ]; then + exit 0 +fi + +# This is slackpkg's exit code when updates are available. +SLACKPKG_UPDATES_PENDING=100 + +slackpkg -mirror="file:///$LOCAL_MIRROR/" check-updates >/dev/null 2>&1 +if [ "$?" -eq "$SLACKPKG_UPDATES_PENDING" ]; then + exit 0 +fi + +# Make the set of package updates available atomically by storing them in a +# temporary location until completion. +rm -fr "$STAGING_DIR" +mkdir --parents "$STAGING_DIR" + +if ! OUTPUT="$( + # Capture this subshell's error output to standard output. + exec 2>&1 + + ( + echo "downloading updates from $SLACKWARE_MIRROR..." + + # Support slackpkg with the least required files for patches. + mkdir --parents "$LOCAL_MIRROR" + cd "$LOCAL_MIRROR" \ + && rsync "$SLACKWARE_MIRROR/CHECKSUMS.md5" . \ + && rsync "$SLACKWARE_MIRROR/CHECKSUMS.md5.asc" . \ + && rsync "$SLACKWARE_MIRROR/ChangeLog.txt" . \ + && rsync "$SLACKWARE_MIRROR/FILELIST.TXT" . \ + && rsync "$SLACKWARE_MIRROR/GPG-KEY" . \ + && rsync "$SLACKWARE_MIRROR/extra" . \ + && rsync "$SLACKWARE_MIRROR/pasture" . \ + && rsync "$SLACKWARE_MIRROR/patches" . \ + && rsync "$SLACKWARE_MIRROR/testing" . \ + && rsync "$SLACKWARE_MIRROR/slackware64/PACKAGES.TXT" slackware64 + ) || exit $? + + slackpkg -mirror="file:///$LOCAL_MIRROR/" check-updates + if [ "$?" -ne "$SLACKPKG_UPDATES_PENDING" ]; then + exit 0 + fi + + ( + # Redirect this subshell's standard output to info file. + exec 1>>"$UPDATE_INFO" + + echo "Slackware updates" + echo + slackpkg show-changelog | diff - "$LOCAL_MIRROR/ChangeLog.txt" + echo + ) +)"; then + >>"$UPDATE_ERROR" echo -e "slackpkg:\n\n$OUTPUT" +fi + +if ! OUTPUT="$( + exec 2>&1 + + if [ ! -f /etc/sbotools/sbotools.conf ] \ + || [ "$(sed -n 's/PKG_DIR=//p' /etc/sbotools/sbotools.conf)" != "$STAGING_DIR" ]; then + # Assume sbotools is disabled. + exit 0 + fi + + UPDATE_INFO=$(mktemp /tmp/sbocheck.XXXXXX) + + (sbocheck | tee $UPDATE_INFO) || exit 1 + if grep -i "no updates available" "$UPDATE_INFO"; then + exit 0 + fi + + sboupgrade \ + --all \ + --noinstall \ + --nointeractive \ + || exit "$?" + + ( + exec 1>>"$UPDATE_INFO" + + echo "Slackbuild updates" + echo + cat "$UPDATE_INFO" + echo + ) +)"; then + if [ -f "$UPDATE_ERROR" ]; then + >>"$UPDATE_ERROR" echo "" + >>"$UPDATE_ERROR" echo "" + fi + + >>"$UPDATE_ERROR" echo -e "sbotools:\n\n$OUTPUT" +fi + +# Make updates atomic. +if [ -f "$UPDATE_ERROR" ]; then + mv "$UPDATE_ERROR" "$PACKAGE_DIR" + mv "$UPDATE_INFO" "$PACKAGE_DIR" + + exit 1 +fi + + +mv "$STAGING_DIR"/* "$PACKAGE_DIR/" +rm -fr "$STAGING_DIR" + +if [ -z "$(find "$PACKAGE_DIR" -name "*.t*z" -or -name "info.txt" -or -name "error.txt")" ]; then + # No updates + exit 0 +fi + +if [ "$NOTIFY" = "yes" ]; then + if [ -f "$UPDATE_ERROR" ]; then + echo "Failures were encountered while trying to download updates" + echo "" + cat "$UPDATE_ERROR" + elif [ -f "$UPDATE_INFO" ]; then + echo "Updates pending installation" + echo "" + cat "$UPDATE_INFO" + fi +fi + +if [ -n "$REBOOT_TIME" ]; then + nohup shutdown -r "$REBOOT_TIME" >/dev/null 2>&1 & +fi diff --git a/src/install-kernel b/src/install-kernel new file mode 100644 index 0000000..42db67a --- /dev/null +++ b/src/install-kernel @@ -0,0 +1,22 @@ +#!/bin/sh +# +# System local command to install the kernel into the system boot loader. +# + +# The /boot/vmlinuz-huge softlink points to the last installed kernel. +KERNEL_VERSION="$(realpath /boot/vmlinuz-huge | sed 's/.*\/.*-\(.*\)/\1/')" +if [ "$1" ]; then + KERNEL_VERSION="$(echo "$1" | sed 's/.*\/.*-\(.*\)/\1/')" +fi + +if [ -z "$KERNEL_VERSION" ]; then + >&2 echo "The kernel version could not be detected from the filename." + exit 1 +fi + +echo "Installing kernel version $KERNEL_VERSION into the efi..." +cp -H /boot/vmlinuz-huge /efi/EFI/Slackware/vmlinuz +if [ -f /etc/mkinitrd.conf ]; then + echo "Installing initialized ram disk into the efi..." + mkinitrd -F -k "$KERNEL_VERSION" >/dev/null +fi diff --git a/src/rc.update b/src/rc.update new file mode 100644 index 0000000..db384ca --- /dev/null +++ b/src/rc.update @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Automatically update your system on reboot using packages in the UPDATE_DIR. +# +# Sometimes there are additional steps to make when your kernel is updated. +# You can define these in a custom 'install-kernel' command which this script +# will run if found. We recommend placing this under /usr/local/sbin. +# + +# A local Slackware mirror with just enough information to support slackpkg. +# +# This step could be skipped if slackpkg supports downloading updates without +# installing them. +LOCAL_MIRROR=${LOCAL_MIRROR:-"/var/cache/slack-autoupdate/mirror"} + +# slackpkg exit code when updates are available. +SLACKPKG_UPDATES_PENDING=100 + +# Where packages are stored pending installation. You can use sub-directories +# to order and group packages. +UPDATE_DIR="/var/spool/slack-autoupdate" + +# Where information about pending updates is stored. +UPDATE_INFO="$UPDATE_DIR/info.txt" + +# Where information about failed updates is stored. +UPDATE_ERROR="$UPDATE_DIR/error.txt" + +UPDATES=$(find "$UPDATE_DIR" -name '*.t*z' | sort) +SLACKPKG_UPDATES="$(slackpkg -mirror="file:///$LOCAL_MIRROR/" check-updates >&-; echo $?)" + +if [ -z "$UPDATES" -a "$SLACKPKG_UPDATES" -ne "$SLACKPKG_UPDATES_PENDING" ]; then + exit 0 +fi + +if read -r -t 5 -p "Installing updates, press enter to skip this process..."; then + exit 0 +fi + +OLD_KERNEL="$(realpath /boot/vmlinuz)" +for PKG in $UPDATES; do + upgradepkg --install-new "$PKG" && + rm "$PKG" +done + +NEW_KERNEL="$(realpath /boot/vmlinuz)" +if [ "$OLD_KERNEL" -ne "$NEW_KERNEL" ]; then + if command -v install-kernel &> /dev/null; then + install-kernel "$NEW_KERNEL" + fi +fi + +# All package updates have been processed. +find "$UPDATE_DIR" -mindepth 1 -not -path "$UPDATE_DIR_IGNORED/*" | xargs rm -fr + +reboot diff --git a/src/test/bin/chmod b/src/test/bin/chmod new file mode 100755 index 0000000..06bd986 --- /dev/null +++ b/src/test/bin/chmod @@ -0,0 +1,2 @@ +#!/bin/bash +exit 0 diff --git a/src/test/bin/chown b/src/test/bin/chown new file mode 100755 index 0000000..06bd986 --- /dev/null +++ b/src/test/bin/chown @@ -0,0 +1,2 @@ +#!/bin/bash +exit 0 diff --git a/src/test/bin/tar b/src/test/bin/tar new file mode 100755 index 0000000..5f3f66b --- /dev/null +++ b/src/test/bin/tar @@ -0,0 +1,2 @@ +#!/bin/bash +exec /usr/bin/tar --no-same-owner -$1 $2 diff --git a/src/test/test_autoupdate.sh b/src/test/test_autoupdate.sh new file mode 100644 index 0000000..e2eaf08 --- /dev/null +++ b/src/test/test_autoupdate.sh @@ -0,0 +1,129 @@ +#/bin/bash +# +# The test assumes you are running Slackware on a 64 bit system. +# + +# Packages are copied here on successful download. +export PACKAGE_DIR="${PACKAGE_DIR:-/var/spool/slack-autoupdate}" + +# Packages are temperarily stored here until success. +export STAGING_DIR="${STAGING_DIR:-/var/cache/slack-autoupdate/staging}" + +# The system's local Slackware mirror. +export LOCAL_MIRROR=${LOCAL_MIRROR:-"/var/cache/slack-autoupdate/mirror"} + +# Use fake chmod and chown commands to avoid errors when making packages. +BIN_DIR="$(realpath $(dirname "${BASH_SOURCE[0]}"))/bin" + +TEST_DIR="$( + mktemp \ + --directory \ + --tmpdir="$TMP" \ + autoupdate.XXXXXX +)" || exit 1 +trap "rm -fr $TEST_DIR" EXIT + +mkdir -p \ + "$TEST_DIR/tmp" \ + "$TEST_DIR/etc/sbotools" \ + "$TEST_DIR/etc/slackpkg" \ + "$TEST_DIR/root" \ + "$TEST_DIR/tmp" \ + "$TEST_DIR/usr/sbo" \ + "$TEST_DIR/var/cache" \ + "$TEST_DIR/var/lib" \ + "$TEST_DIR/var/lock" \ + "$TEST_DIR/var/log" \ + "$TEST_DIR/var/spool" + +if ! command -v bwrap &> /dev/null +then + echo "bwrap could not be found. The 'bubblewrap' application must be installed to run this test." + exit 1 +fi + +# Slackpkg etc. require a fake root user and filesystem. +export INSTANCE="\ +bwrap \ + --unshare-user --uid 0 --gid 0 \ + --setenv "HOME" "/root" \ + --ro-bind / / \ + --dev /dev \ + --bind "$TEST_DIR/tmp" "/tmp" \ + --bind "$TEST_DIR/etc/sbotools" /etc/sbotools \ + --bind "$TEST_DIR/etc/slackpkg" /etc/slackpkg \ + --bind "$TEST_DIR/root" /root \ + --bind "$BIN_DIR" /usr/local/bin \ + --bind "$TEST_DIR/usr/sbo" /usr/sbo \ + --bind "$TEST_DIR/var" /var \ + --" + +$INSTANCE ln -s ../lib/pkgtools/packages "/var/log/packages" + +echo "Preparing slackpkg test case" +( + mkdir -p "$TEST_DIR/etc" + cp --recursive /etc/slackpkg "$TEST_DIR/etc/" + + echo http://mirrors.kernel.org/slackware/slackware64-14.2/ > "$TEST_DIR/etc/slackpkg/mirrors" + echo "WGETFLAGS=\"-4 --quiet\"" >> "$TEST_DIR/etc/slackpkg/slackpkg.conf" # REMOVE ME + yes | $INSTANCE slackpkg update >/dev/null || exit 1 + echo http://mirrors.kernel.org/slackware/slackware64-15.0/ > "$TEST_DIR/etc/slackpkg/mirrors" +) + +echo "Preparing sbotools test case" +( + $INSTANCE sboconfig --pkg-dir "$STAGING_DIR" >/dev/null + + mkdir -p "$TEST_DIR/var/lib/pkgtools/packages" + touch "$TEST_DIR/var/lib/pkgtools/packages/bubblewrap-0.7.0-x86_64-1_SBo" + $INSTANCE sbosnap fetch +) + +echo "" +echo "Running autoupdate..." +echo "" + +# Display a helpful message and successful exit code by tracking total success. +TESTS_PASS=true + +if ! $INSTANCE bash $(dirname "${BASH_SOURCE[0]}")/../autoupdate; then + TESTS_PASS=false + + echo "autoupdate failed" + cat "$TEST_DIR/$PACKAGE_DIR/error.txt" +elif [ -f "$TEST_DIR/$PACKAGE_DIR/error.txt" ]; then + TESTS_PASS=false + + cat "$TEST_DIR/$PACKAGE_DIR/error.txt" +fi + +if [ ! -f "$TEST_DIR/$PACKAGE_DIR/info.txt" ]; then + TESTS_PASS=false + + echo "expected '$PACKAGE_DIR/info.txt' file to be created" +fi + +$INSTANCE slackpkg -mirror="file:///$LOCAL_MIRROR/" check-updates >/dev/null +if [ "$?" -ne 100 ]; then + TESTS_PASS=false + + echo "slackpkg: expected to have updates available but had exit code $?" +else + echo "slackpkg: ok" +fi + +if [ -f "$TEST_DIR/etc/sbotools/sbotools.conf" ] \ + && [ -z "$(find "$TEST_DIR/$PACKAGE_DIR" -name 'bubblewrap-*.t*z')" ]; then + TESTS_PASS=false + + echo "sbotools: expected to have bubblewrap package update" +else + echo "sbotools: ok" +fi + +if [ "$TESTS_PASS" = true ]; then + echo "all tests pass" +else + exit 1 +fi |