aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE.txt20
-rw-r--r--README.md107
-rw-r--r--src/autoupdate186
-rw-r--r--src/install-kernel22
-rw-r--r--src/rc.update56
-rwxr-xr-xsrc/test/bin/chmod2
-rwxr-xr-xsrc/test/bin/chown2
-rwxr-xr-xsrc/test/bin/tar2
-rw-r--r--src/test/test_autoupdate.sh129
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