#!/bin/bash # Pre-update maintenance notification. Runs as a systemd oneshot service, well # before apt-daily-upgrade.timer's own window, and tells you (Mail + Matrix) # which packages are about to be auto-upgraded - so a post-update problem can # immediately be traced back to "the update ran, that's probably it". # # Generic by design: no instance-specific values are hardcoded here. All of # them live in /etc/maintenance-notify/config - see config.example in this # same directory, and docs/deployment-guides/07-host-maintenance-notifications.md # for the full setup guide. set -euo pipefail CONFIG_FILE="/etc/maintenance-notify/config" MATRIX_TOKEN_FILE="/etc/maintenance-notify/matrix-token" if [ ! -f "$CONFIG_FILE" ]; then echo "Missing $CONFIG_FILE - see docs/deployment-guides/07-host-maintenance-notifications.md" >&2 exit 1 fi # shellcheck source=/dev/null . "$CONFIG_FILE" : "${MATRIX_HOMESERVER:?MATRIX_HOMESERVER not set in $CONFIG_FILE}" : "${MATRIX_ROOM_ID:?MATRIX_ROOM_ID not set in $CONFIG_FILE}" : "${MATRIX_THREAD_EVENT_ID:?MATRIX_THREAD_EVENT_ID not set in $CONFIG_FILE}" : "${MAIL_FROM:?MAIL_FROM not set in $CONFIG_FILE}" : "${MAIL_TO:?MAIL_TO not set in $CONFIG_FILE}" apt-get update -qq DRYRUN_OUTPUT="$(unattended-upgrade --dry-run -v 2>&1)" # Exact log strings taken from /usr/bin/unattended-upgrade itself (verified # live on the target host), not guessed - this is the one message emitted # when there is nothing to do, and the one line emitted with the package # list otherwise. They're mutually exclusive. if echo "$DRYRUN_OUTPUT" | grep -q "No packages found that can be upgraded unattended"; then echo "No pending upgrades - nothing to notify." exit 0 fi PENDING_PKGS="$(echo "$DRYRUN_OUTPUT" | sed -n 's/^.*Packages that will be upgraded: //p' | tail -1)" if [ -z "$PENDING_PKGS" ]; then echo "No pending upgrade packages parsed - nothing to notify." exit 0 fi HOST_LABEL="$(hostname -f 2>/dev/null || hostname)" NOW="$(date '+%Y-%m-%d %H:%M %Z')" BODY="Host: ${HOST_LABEL} Zeitpunkt: ${NOW} Im naechsten apt-daily-upgrade.timer-Fenster werden folgende Pakete automatisch aktualisiert: ${PENDING_PKGS} Automatische Vorab-Benachrichtigung, keine Aktion erforderlich." send_mail() { if ! command -v msmtp >/dev/null 2>&1; then echo "msmtp not installed, skipping mail notification" >&2 return 1 fi { echo "From: ${MAIL_FROM}" echo "To: ${MAIL_TO}" echo "Subject: [${HOST_LABEL}] Anstehendes Update" echo echo "$BODY" } | msmtp -a maintenance-notify -- "${MAIL_TO}" } send_matrix() { if [ ! -f "$MATRIX_TOKEN_FILE" ]; then echo "Missing $MATRIX_TOKEN_FILE, skipping Matrix notification" >&2 return 1 fi local token txn_id encoded_room payload token="$(cat "$MATRIX_TOKEN_FILE")" txn_id="$(uuidgen)" encoded_room="$(jq -rn --arg s "$MATRIX_ROOM_ID" '$s|@uri')" payload="$(jq -n --arg body "$BODY" --arg event_id "$MATRIX_THREAD_EVENT_ID" \ '{msgtype: "m.text", body: $body, "m.relates_to": {rel_type: "m.thread", event_id: $event_id}}')" curl -sS -f -X PUT \ -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json" \ --data "$payload" \ "${MATRIX_HOMESERVER}/_matrix/client/v3/rooms/${encoded_room}/send/m.room.message/${txn_id}" \ > /dev/null } mail_ok=0 matrix_ok=0 send_mail && mail_ok=1 send_matrix && matrix_ok=1 if [ "$mail_ok" -eq 0 ] && [ "$matrix_ok" -eq 0 ]; then echo "Both mail and Matrix notification failed" >&2 exit 1 fi