Automatische Updates von opensuse
Automatische Updates gibt es für opensuse mit dem opensuse-online-Update schon lange. Diese werden automatisiert zu einer vorgegebenen Zeit durchgeführt. Für einen Laptop, der immer zu unterschiedlichen Zeiten verwendet wird, ist das aber eher ungeeignet, da der Laptop meist nicht z.B. morgens um 06:00 Uhr läuft. Hier ist eine Aktualisierung sinnvoll, die beim Herunterfahren des Gerätes die vorhandenen Updates installiert.
Idealerweise sollten die Updates direkt nach dem Start installiert werden. Das bedingt aber eine Wartezeit für den Anwender, bevor der Rechner verwendet werden kann. Und in der Regel schaltet man den Rechner ja auch erst dann an, wenn man ihn dringend benötigt. Da ist so eine Wartezeit eher hinderlich.
Nun wird sicherlich jemand kommen und sagen, dass Patches doch durch den Anwender selbst zu einer ihm genehmen Zeit installiert werden sollten. Das ist aber für Menschen, die mit der zugrunde liegenden Technik eher auf Kriegsfuß stehen, eher eine Last. Da werden die notwendigen Updates dann auch gerne mal ein halbes Jahr ignoriert. Das kann in der heutigen Zeit schnell kritisch werden.
Mit der Anregung von Kegan Edwards Zypper Automatic Update System habe ich daher eine konfigurierbare Lösung erarbeitet. Dabei kann durch Einstellungen festgelegt werden, ob das Update mit „zypper up“ (Patchinstallation bei Leap) oder mit „zypper dup“ (Updates bei Tumbleweed) erfolgen soll. Ebenso kann festgelegt werden, ob Recommended Patches installiert oder übersprungen werden sollen.
Zusätzlich sollen E-Mails versandt werden, wenn eine Adresse angegeben ist, und das Update durchgeführt wurde.
Komponenten des Updates
- Download der verfügbaren Patches
- Installation der heruntergeladenen Patches beim Shutdown oder Reboot
- Benachrichtigung der Anwender
- Konfiguration des Installationsmodus und -umfanges
Download der verfügbaren Patches
Hier werden mittels zypper die Paketquellen aktualisiert und die notwendigen Patches heruntergeladen. Dazu wird ein Service erstellt, der nach der Verfügbarkeit des Netzwerkes die notwendigen Aktionen ausführt. Für den Fall, dass der Rechner länger eingeschaltet ist, wiederholt sich dies alle 24 Stunden.
Aktualisierung der Paketquellen und laden der Patches
- /usr/local/bin/zypper-refresh-download.sh
#!/bin/bash # update mode for zypper # dup or up ZYPPER_OFFLINE_UPDATE_MODE=up # include recommended patches # yes or no ZYPPER_OFFLINE_UPDATE_RECOMENDS=no ZYPPER_OFFLINE_UPDATE_STATUS_FILE=/var/run/zypper-update-triggered if [ -e /etc/sysconfig/zypper-offline-update ]; then . /etc/sysconfig/zypper-offline-update fi if [ $ZYPPER_OFFLINE_UPDATE_RECOMENDS = no ]; then NORECOMMENDS=--no-recommends fi function log() { LEVEL=$1 MESSAGE=$2 echo $MESSAGE echo $MESSAGE | systemd-cat -t zypper-auto-update -p $LEVEL } # Refresh repositories /usr/bin/zypper refresh # Download updates (non-interactive) without changing recommendations and other specified options if /usr/bin/zypper $ZYPPER_OFFLINE_UPDATE_MODE -y $NORECOMMENDS --download-only; then # Create a flag file to indicate the update was triggered and completed successfully echo 1 > $ZYPPER_OFFLINE_UPDATE_STATUS_FILE log "info" "Update download completed successfully" else echo 2 > $ZYPPER_OFFLINE_UPDATE_STATUS_FILE log "err" "Update download failed" exit 1 fi # Ensure the service exits cleanly exit 0
Service für die Aktualisierung der Paketquellen und laden der Patches
- /etc/systemd/system/zypper-refresh-download.service
[Unit] Description=Zypper Refresh and Download Updates (Non-interactive) Wants=network-online.target zypper-offline-update.service After=network-online.target [Service] Type=oneshot RemainAfterExit=yes TimeoutStartSec=0 ExecStartPre=/bin/sleep 10 ExecStart=/usr/local/bin/zypper-refresh-download.sh Environment="ZYPP_LOCK_TIMEOUT=600" [Install] WantedBy=multi-user.target
Timer für die Aktualisierung der Paketquellen und laden der Patches
- /etc/systemd/system/zypper-refresh-download.timer
[Unit] Description=Run Zypper Refresh and Download Updates daily [Timer] OnBootSec=10m OnUnitActiveSec=24h RandomizedDelaySec=2h [Install] WantedBy=timers.target
Offline Update Service
Der Offline Update Service installiert Patches, wenn das System in einem sauberen Zustand ist und der Anwender nicht mehr durch Abbrüche von Anwendungen gestört werden kann. Es werden nur dann Patches installiert, wenn das Herunterladen fehlerfrei erfolgt ist.
Installation der Patches
- /usr/local/bin/zypper-offline-update.sh
#!/bin/bash # update mode for zypper # dup or up ZYPPER_OFFLINE_UPDATE_MODE=up # include recommended patches # yes or no ZYPPER_OFFLINE_UPDATE_RECOMENDS=no ZYPPER_OFFLINE_UPDATE_STATUS_FILE=/var/run/zypper-update-triggered INSTALL_LOG=/tmp/zypper-offline-update.log #pid file PIDFILE=/var/run/zypper-offline-update.pid if [ -e /etc/sysconfig/zypper-offline-update ]; then . /etc/sysconfig/zypper-offline-update fi if [ $ZYPPER_OFFLINE_UPDATE_RECOMENDS = no ]; then NORECOMMENDS=--no-recommends fi function usage() { echo "usage:" echo "$0 start Start service for waiting to shutdown" echo "$0 stop Run updates before shutdown" echo "$0 drystop Run without installing anything - for testing purposes only" exit 1 } function handle_term_signal() { exit 0 } function log() { LEVEL=$1 MESSAGE=$2 echo $MESSAGE echo $MESSAGE | systemd-cat -t zypper-auto-update -p $LEVEL if [ ! -z $ZYPPER_OFFLINE_UPDATE_MAIL_TO ]; then HOST=$(hostname) RUNDATE=$(date '+%d.%m.%Y %H:%M:%S') LOGDATA="no log output" if [ -e $INSTALL_LOG ]; then LOGDATA=$(cat $INSTALL_LOG) fi mailx -s "$HOST $LEVEL: $MESSAGE" $ZYPPER_OFFLINE_UPDATE_MAIL_TO <<EOF This mail is for information only. The zypper-offline-update process on $HOST was run on $RUNDATE. Result of offline update is: $MESSAGE LOG from zypper: ================ $LOGDATA EOF echo "sent email!" fi } trap handle_term_signal SIGTERM if [ X$1 = "X" ]; then usage else action=$1 fi if [ $action = "start" ]; then nohup $0 run & fi if [ $action = "run" ]; then echo $$ > $PIDFILE while true do sleep 1 done fi DOWNLOADSTATE=0 if [ $action = "stop" -o $action = "drystop" ]; then # Check if updates were downloaded successfully if [ -f $ZYPPER_OFFLINE_UPDATE_STATUS_FILE ]; then DOWNLOADSTATE=$(cat $ZYPPER_OFFLINE_UPDATE_STATUS_FILE) fi if [ $DOWNLOADSTATE -eq 1 ]; then # Apply updates if [ $action = "drystop" ]; then echo "dry run has no output from zypper" > $INSTALL_LOG log "info" "Dry run of offline updates" else /usr/bin/zypper $ZYPPER_OFFLINE_UPDATE_MODE -y $NORECOMMENDS > $INSTALL_LOG ZYPPERSTATE=$? cat $INSTALL_LOG if [ $ZYPPERSTATE -eq 103 ]; then log "info" "The Zypper package was patched, rerunning update to apply remaining patches." rm $INSTALL_LOG /usr/bin/zypper $ZYPPER_OFFLINE_UPDATE_MODE -y $NORECOMMENDS > $INSTALL_LOG ZYPPERSTATE=$? cat $INSTALL_LOG fi if [ $ZYPPERSTATE -eq 0 ]; then log "info" "Offline update applied successfully" else log "err" "Offline update failed" fi # Remove the trigger file rm $ZYPPER_OFFLINE_UPDATE_STATUS_FILE fi fi if [ $DOWNLOADSTATE -eq 2 ]; then log "info" "Download of updates failed or download was incomplete" fi if [ $DOWNLOADSTATE -eq 0 ]; then log "info" "Update service not running properly" fi if [ $action = "stop" ]; then # clean up rm -f $ZYPPER_OFFLINE_UPDATE_STATUS_FILE # stop service process and cleanup pid=$(cat $PIDFILE) rm $PIDFILE kill $pid fi if [ -e $INSTALL_LOG ]; then rm $INSTALL_LOG fi # wait for sending e-mail sleep 1 # Ensure the service exits cleanly exit 0 fi
Service zur Installation der Patches
- /etc/systemd/system/zypper-offline-update.service
[Unit] Description=Zypper Offline Update Wants=network-online.target postfix.service After=network-online.target postfix.service Requires=network-online.target postfix.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/local/bin/zypper-offline-update.sh start ExecStop=/usr/local/bin/zypper-offline-update.sh stop TimeoutStopSec=900 [Install] WantedBy=multi-user.target
Benachrichtigung der Anwender
Benachrichtigt Anwender über den Updatestatus beim Login.
Benachrichtigung der Anwender über Installation der Patches
- /usr/local/bin/zypper-update-notify.sh
#!/bin/bash # Get the current user user=$USER # Ensure we have a user if [ -z "$user" ]; then echo "No user specified" | systemd-cat -t zypper-auto-update -p err exit 1 fi # Wait a few seconds for the session to be fully initialized sleep 5 # Check if either service failed if systemctl is-failed --quiet zypper-refresh-download.service || \ systemctl is-failed --quiet zypper-offline-update.service; then sudo -u $user DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $user)/bus \ notify-send -a "Zypper Auto Update" -u critical "Zypper Update Failed" "One of the Zypper services has failed. Please check the logs." else # Optionally notify of success sudo -u $user DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $user)/bus \ notify-send -a "Zypper Auto Update" "Zypper Update Status" "System updates are working normally." fi exit 0
Autostart Konfiguration für Benachrichtigung der Anwender über den Status der Auto Update Prozesse
- /etc/xdg/autostart/zypper-update-notification.desktop
[Desktop Entry] Name=Zypper Update Notification Name[de]=Zypper Update Benachrichtigung Exec=/usr/local/bin/zypper-update-notify.sh Icon=system-software-update Type=Application NoDisplay=true X-KDE-autostart-phase=1 OnlyShowIn=KDE
Gegebenenfalls ist noch das Paket libnotify-tools zu installieren. Fehlt dieses Paket, so schlägt die Benachrichtigung fehl, da das Programm notify-send nicht vorhanden ist.
zypper install libnotify-tools
Konfigurationsdatei
Konfigurationsdatei für die Installation der Patches
- /etc/sysconfig/zypper-offline-update
# update mode for zypper # dup or up ZYPPER_OFFLINE_UPDATE_MODE=up # include recommended patches # yes or no ZYPPER_OFFLINE_UPDATE_RECOMENDS=yes # status file for downloads of offline updates ZYPPER_OFFLINE_UPDATE_STATUS_FILE=/var/run/zypper-update-triggered # send update status as e-mail # leave blank for no email ZYPPER_OFFLINE_UPDATE_MAIL_TO=
Installation
Alle Dateien werden wie oben dargestellt angelegt. Anschließend müssen noch die Ausführungsrechte gesetzt werden:
chmod +x /usr/local/bin/zypper-refresh-download.sh chmod +x /usr/local/bin/zypper-offline-update.sh chmod +x /usr/local/bin/zypper-update-notify.sh
Nun sind die Dienste noch zu aktivieren:
systemctl daemon-reload systemctl enable zypper-refresh-download.timer systemctl enable zypper-refresh-download.service systemctl enable zypper-offline-update.service systemctl enable zypper-update-notify.service systemctl start zypper-refresh-download.timer
Gegebenenfalls ist noch die Konfigurationsdatei /etc/sysconfig/zypper-offline-update an die persönlichen Bedürfnisse anzupassen.
Nach der Installation sollten noch die Status der einzelnen Dienste geprüft werden. Siehe dazu unter Fehlerbehebung.
Fehlerbehebung
Die Status der Dienste können wie folgt geprüft werden:
# Check timer status systemctl status zypper-refresh-download.timer # Check download service status systemctl status zypper-refresh-download.service # Check offline update service status systemctl status zypper-offline-update.service # Check notification service status systemctl status zypper-update-notify.service
Die Protokolle können wie folgt untersucht werden:
# View all related logs journalctl -t zypper-auto-update # View specific service logs journalctl -u zypper-refresh-download.service journalctl -u zypper-offline-update.service journalctl -u zypper-update-notify.service