Files
2026-02-16 15:05:15 +01:00

582 lines
15 KiB
Bash

#!/bin/zsh
#
# Felix Danckwerts, 2019
#
# Parameters:
# immediaterestart: is set, then a restart will be done immediate, if necessary
#
# limit to only run once
# read information from sus
#
#todo:
# Install not only recommended updates
# sus request
# Postpone check
# fv encrypting
# T2 halt
# restart: wait for jamf to finish
version=0.14
########
## funnctions
########
date_get() {
/bin/date +"%Y-%m-%d %H:%M:%S"
}
log_write() {
# $1 : Text, which is written to the log
# $2 : LogMode. If LogMode = "v", then the entry will only be logged, if global variable "$LOGMODE" = "v". (Optional)
if [ -z "${2}" ] || [ "${LOGMODE}" = "v" ]; then
echo "$(date_get) ${1}" >> "${LOG_FILENAME}"
/usr/bin/logger -s "${LOGGER_TAG} - ${1}"
fi
}
pref_read() {
# $1: Key
defaults read "${PREFERENCESDOMAIN_READ}" "${1}" 2> /dev/null
}
pref_readlocal() {
# $1: Key
defaults read "${PREFERENCESDOMAIN_LOCAL}" "${1}" 2> /dev/null
}
pref_writelocal() {
# $1: Key
# $2: Type or Value
# $3: Value (opt)
defaults write "${PREFERENCESDOMAIN_LOCAL}" "${1}" "${2}" "${3}" 2> /dev/null
}
gui_dialog_show() {
# $1: text
# $2: title
# $3: button 1 text
# $4: button 2 text (opt)
local description="${1}"
local title="${2}"
local parameters=""
[ -n "${3}" ] && local parameters="-button1 \"${3}\" -defaultButton 1"
[ -n "${4}" ] && local parameters="${parameters} -button2 \"${4}\" -cancelButton 2"
"${GUITOOL_JAMFHELPER}" -windowType utility -icon "${ICON_SU}" -title "${title}" -description "${description}" ${=parameters} 2> /dev/null > /dev/null
local errl=$?
GUITOOL_JAMFHELPER_DIALOG_PID=$!
return $errl
}
gui_dialog_close() {
[ -n "${GUITOOL_JAMFHELPER_DIALOG_PID}" ] && kill ${GUITOOL_JAMFHELPER_DIALOG_PID}
}
gui_hud_show() {
# $1: text
# $2: title
# $3: windows position (opt)
local description="${1}"
local title="${2}"
[ -n "${3}" ] && local parameters="-windowPosition ${3}"
"${GUITOOL_JAMFHELPER}" -windowType hud -lockHUD ${=parameters} \
-title "${title}" -description "${description}" -icon "${ICON_SU}" &>/dev/null &
GUITOOL_JAMFHELPER_HUD_PID=$!
}
gui_hud_close() {
[ -n "${GUITOOL_JAMFHELPER_HUD_PID}" ] && kill ${GUITOOL_JAMFHELPER_HUD_PID}
}
list_additem() {
local list="${1}"
local item="${2}"
if [ -n "${list}" ]; then
echo "${list}\n${item}"
else
echo "${item}"
fi
}
positivelist_get() {
local url="${1}"
local auth="${2}"
if [ -n "${auth}" ]; then
curl -su "${auth}" "${url}" -o "${POSITIVELIST_OUTPUTFILE}"
else
curl -s "${url}" -o "${POSITIVELIST_OUTPUTFILE}"
fi
}
positivelist_check() {
local listentry=$1
if [ -n "$(cat "${POSITIVELIST_OUTPUTFILE}" | tr " " "^" | grep "${listentry}")" ]; then
return 0
else
return 1
fi
}
positivelist_removefromlist() {
local list=$1
local newlist=""
for listentry in ${=list}
do
if [ -n "${listentry}" ]; then
listentry_identifier=$(echo "${listentry}" | cut -d ";" -f1)
#listentry_productkey=$(echo "${listentry}" | cut -d ";" -f2 | tr "^" " ")
positivelist_check "${listentry_identifier}"
if [ $? -gt 0 ] ; then
newlist=$(list_additem "${newlist}" "${listentry}")
fi
fi
done
echo ${newlist}
}
softwareupdate_resetignored() {
softwareupdate --reset-ignored 2>&1 > /dev/null
}
softwareupdate_ignore() {
softwareupdate --ignore "${1}" 2>&1 > /dev/null
}
softwareupdate_install_inbackground() {
/usr/sbin/softwareupdate --install --all ${1} 2> /dev/null &
echo "$!" > "${SOFTWAREUPDATE_PIDFILE}"
}
softwareupdate_list_get_inbackground() {
/usr/sbin/softwareupdate --list > "${SOFTWAREUPDATE_OUTPUTFILE}" 2> /dev/null &
echo "$!" > "${SOFTWAREUPDATE_PIDFILE}"
}
softwareupdate_kill_backgroudprocess() {
kill $(cat "${SOFTWAREUPDATE_PIDFILE}")
}
softwareupdate_restart_check() {
if [ -n "$(grep "restart" "${SOFTWAREUPDATE_OUTPUTFILE}")" ]; then
return 0
else
return 1
fi
}
softwareupdate_waitforcompletion() {
local timeout=${1}
if [ -e "${SOFTWAREUPDATE_PIDFILE}" ]; then
sleep 1
while [ ${timeout} -gt 0 ]
do
/usr/bin/pgrep -F "${SOFTWAREUPDATE_PIDFILE}" >/dev/null
if [ $? -gt 0 ]; then
break
fi
local timeout=$((timeout-1))
sleep 1
done
fi
echo ${timeout}
if [ ${timeout} -le 0 ]; then
return 1
else
local errl=$(/usr/bin/defaults read "${SOFTWAREUPDATE_PLISTFILE}" LastResultCode)
if [ ${errl} -eq 1 ]; then
return 0
else
return $errl
fi
fi
}
softwareupdate_postpone_check() {
# return: postponed (int)
local postponed=$(pref_readlocal "${PREFERENCESDOMAIN_KEY_POSTPONED}")
[ -z "${postponed}" ] && local postponed=0
if [ ${postponed} -ge ${MAX_POSTPONED_ALLOWED} ]; then
return 1
else
return 0
fi
echo ${postponed}
}
softwareupdate_list_read() {
local suid=0
local list=""
for updatetype in RecommendedUpdates
do
while :
do
Identifier=$(/usr/libexec/PlistBuddy "${SOFTWAREUPDATE_PLISTFILE}" -c "print \"${updatetype}:${suid}:Identifier\"" 2> /dev/null)
if [ $? -eq 0 ]; then
ProductKey=$(/usr/libexec/PlistBuddy "${SOFTWAREUPDATE_PLISTFILE}" -c "print \"${updatetype}:${suid}:Product Key\"" 2> /dev/null)
list="${list}$(echo "${Identifier}" | tr " " "^");$(echo "${ProductKey}" | tr " " "^") "
else
break
fi
suid=$((suid+1))
done
done
echo "${list}"
}
startup() {
log_write "starting ${SCRIPTNAME} - version ${version}"
[ ! -e "${GUITOOL_JAMFHELPER}" ] && log_write "warning - Gui-Tool not found at ${GUITOOL_JAMFHELPER} - user cannot be informed prior to a restart"
if [ -e "${SUCONTROL_PIDFILE}" ]; then
/usr/bin/pgrep -F "${SUCONTROL_PIDFILE}" >/dev/null
if [ $? -eq 0 ]; then
log_write "error - Script is running in another process - exiting"
exit 1
else
log_write "warning - PID file of script found"
fi
fi
echo "${SCRIPTPID}" > "${SUCONTROL_PIDFILE}"
softwareupdate_resetignored
}
cleanup() {
rm -f "${SOFTWAREUPDATE_OUTPUTFILE}"
rm -f "${POSITIVELIST_OUTPUTFILE}"
rm -f "${SOFTWAREUPDATE_PIDFILE}"
rm -f "${SUCONTROL_PIDFILE}"
log_write "end of ${SCRIPTNAME}"
}
reboot_do() {
local immediaterestart=${1}
if [ "${immediaterestart}" = "true" ]; then
log_write "reboot immediate"
/usr/bin/nohup /bin/zsh -c "reboot" 2>&1 >/dev/null &
# stopping scrept here
sleep 600
else
log_write "reboot delayed"
/usr/bin/nohup /bin/zsh -c "sleep ${TIMEOUT_RESTART} ; reboot" 2>&1 >/dev/null &
fi
# /usr/bin/nohup /bin/zsh -c "sleep 30 ; /usr/bin/osascript -e \"tell application \\\"System Events\\\" to restart\"" 2>&1 >/dev/null &
}
user_loggedin_check() {
local loggedinUser="$(/usr/bin/stat -f ""%Su"" /dev/console)"
if [ -n "${loggedinUser}" ] && [ "${loggedinUser}" != "root" ]; then
return 0
else
return 1
fi
}
########
## const var
########
SCRIPTNAME="${0}"
SCRIPTPID=$$
LOGMODE="v"
LOGGER_TAG="sucontrol"
mkdir -p "/var/vwgenrollment/log"
LOG_FILENAME="/var/vwgenrollment/log/sucontrol-$(/bin/date +%s).log"
SUCONTROL_PIDFILE="/tmp/sucontrol_script.pid"
RESTART_IMMEDIATE=false
for param in ${4} ${5} ${6} ${7} ${8} ${9} ${10}
do
case ${param} in
immediaterestart)
RESTART_IMMEDIATE=true
;;
esac
done
PREFERENCESDOMAIN_LOCAL="/Library/Preferences/de.autostadt.sucontrol.plist"
PREFERENCESDOMAIN_CP="/Library/Managed Preferences/de.autostadt.sucontrol.plist"
PREFERENCESDOMAIN_KEY_LASTRUNDATE="LastRunDate"
PREFERENCESDOMAIN_KEY_LASTSUCCESSFULLDATE="LastSuccessfullDate"
PREFERENCESDOMAIN_KEY_LASTRESULT="LastResult"
PREFERENCESDOMAIN_KEY_LASTPOSITIVELIST="LastPositiveList"
PREFERENCESDOMAIN_KEY_POSTPONED="Postponed"
if [ -e "${PREFERENCESDOMAIN_CP}" ]; then
PREFERENCESDOMAIN_READ="${PREFERENCESDOMAIN_CP}"
else
PREFERENCESDOMAIN_READ="${PREFERENCESDOMAIN_LOCAL}"
fi
GUITOOL_JAMFHELPER="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
GUITOOL_JAMFHELPER_DIALOG_PID=""
GUITOOL_JAMFHELPER_HUD_PID=""
ICON_SU="/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns"
SOFTWAREUPDATE_PIDFILE="/tmp/sucontrol_softwareupdate.pid"
SOFTWAREUPDATE_PLISTFILE="/Library/Preferences/com.apple.SoftwareUpdate.plist"
SOFTWAREUPDATE_OUTPUTFILE=$(/usr/bin/mktemp -t sucontrol)
POSITIVELIST_OUTPUTFILE=$(/usr/bin/mktemp -t sucontrol)
TIMEOUT_SOFTWAREUPDATELIST=120
TIMEOUT_SOFTWAREUPDATEINSTALL=3600
TIMEOUT_RESTART=45
#log_write "SOFTWAREUPDATE_OUTPUTFILE: ${SOFTWAREUPDATE_OUTPUTFILE}" "v"
#log_write "POSITIVELIST_OUTPUTFILE: ${POSITIVELIST_OUTPUTFILE}" "v"
TEXT_1=$(pref_read "Text_1")
[ -z "${TEXT_1}" ] && TEXT_1="Es wird ein Softwareupdate installiert, das einen Neustart des Rechners verlangt.
Bitte beenden Sie alle Programme und klicken danach auf \"Update\". Vor dem Neustart des Rechners wird ein weiterer Hinweis angezeigt."
TEXT_1_2=$(pref_read "Text_1_2")
[ -z "${TEXT_1_2}" ] && TEXT_1_2="Es wurden Updates installiert, die einen Neustart des Rechners erforderlich machen.
Bitte beenden Sie alle Programme und klicken danach auf \"Restart\". Nicht gesicherte Dokumente gehen verloren."
TEXT_2=$(pref_read "Text_2")
[ -z "${TEXT_2}" ] && TEXT_2="SoftwareUpdate wird durchgeführt."
TEXT_3=$(pref_read "Text_3")
[ -z "${TEXT_3}" ] && TEXT_3="SoftwareUpdate wird durchgeführt.
Nach erfolgreichem Update wird ein Neustart initiiert."
TEXT_4=$(pref_read "Text_4")
[ -z "${TEXT_4}" ] && TEXT_4="Ein Neustart wird durchgeführt!"
BUTTON_1=$(pref_read "Button_1")
[ -z "${BUTTON_1}" ] && BUTTON_1="Update"
BUTTON_2=$(pref_read "Button_2")
[ -z "${BUTTON_2}" ] && BUTTON_2="Verschieben"
BUTTON_3=$(pref_read "Button_3")
[ -z "${BUTTON_3}" ] && BUTTON_3="Neustart"
BUTTON_4=$(pref_read "Button_4")
[ -z "${BUTTON_4}" ] && BUTTON_4="Ein Neustart wird durchgeführt!"
MAX_POSTPONED_ALLOWED=$(pref_read "Max_Postponed_Allowed")
[ -z "${MAX_POSTPONED_ALLOWED}" ] && MAX_POSTPONED_ALLOWED=1000
########
## main
########
startup
#softwareupdate_list_get_inbackground
#softwareupdate_waitforcompletion 100
#if [ $? -eq 0 ]; then
#softwareupdate_list_read
#else
#echo fail
#fi
#exit 0
# read the positive list from webserver
positiveListeUrl=$(pref_read URL)
if [ -n "${positiveListeUrl}" ]; then
log_write "reading Positive list from URL ${positiveListeUrl}"
positiveListeAuth=$(pref_read auth)
positivelist_get "${positiveListeUrl}" "${positiveListeAuth}"
if [ $? -gt 0 ] || [ -n "$(grep "Unauthorized" "${POSITIVELIST_OUTPUTFILE}")" ]; then
log_write "error: could not read positive list - exiting"
cleanup
exit 1
fi
else
log_write "error: could not read URL for positive list - exiting"
cleanup
exit 1
fi
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTPOSITIVELIST}" "-string" "$(cat "${POSITIVELIST_OUTPUTFILE}" | tr " \n" "^,")"
# echo positiveList:
# echo $positiveList
ignoreList=""
loopCounter=20
# check for softwareupdates in a loop until no new softwareupdates are returned
while [ ${loopCounter} -gt 0 ]
do
log_write "reading software update list (backgrounded, timeout ${TIMEOUT_SOFTWAREUPDATELIST} sec)"
# read the list of softwareupdates
softwareupdate_list_get_inbackground
waitedTime=$(softwareupdate_waitforcompletion ${TIMEOUT_SOFTWAREUPDATELIST})
errl=$?
if [ ${errl} -eq 0 ] || [ ${waitedTime} -gt 0 ] ; then
log_write "software update list read in $((TIMEOUT_SOFTWAREUPDATELIST-waitedTime+1)) sec"
softwareUpdateList=$(softwareupdate_list_read)
log_write "software update list:" "v"
log_write "${softwareUpdateList}" "v"
# remove all softwareupdates, which are onn the positive list from the list of updates
softwareUpdateList_filtered=$(positivelist_removefromlist "${softwareUpdateList}")
log_write "software update list filtered:" "v"
log_write "${softwareUpdateList_filtered}" "v"
# go on, if there are software updates left onn the list, otherwise leave the loop
if [ -n "${softwareUpdateList_filtered}" ]; then
for listentry in ${=softwareUpdateList_filtered}
do
listentry_identifier=$(echo "${listentry}" | cut -d ";" -f1 | tr "^" " ")
listentry_productkey=$(echo "${listentry}" | cut -d ";" -f2 | tr "^" " ")
log_write "adding \"${listentry_identifier}\" \"${listentry_productkey}\" to Softwareupdates ignore list"
softwareupdate_ignore "${listentry_identifier}"
ignoreList=$(list_additem "${ignoreList}" "${listentry_identifier}")
done
else
break
fi
loopCounter=$((loopCounter-1))
else
softwareupdate_kill_backgroudprocess
log_write "error - could not read softwareupdate list"
cleanup
exit 1
fi
done
#echo "ignoreList:"
#echo "$ignoreList"
# go on, if the last read software update list (not the filtered one) contains any entries
if [ -n "${softwareUpdateList}" ]; then
# show gui only, if a user is logged in
user_loggedin_check
if [ $? -eq 0 ]; then
log_write "a user is logged in - show GUI"
softwareupdate_restart_check
if [ $? -eq 0 ]; then
guiText="${TEXT_3}"
postponed=$(softwareupdate_postpone_check)
if [ $? -eq 0 ] ; then
gui_dialog_show "${TEXT_1}" "SoftwareUpdate" "${BUTTON_1}" "${BUTTON_2}"
else
gui_dialog_show "${TEXT_1}" "SoftwareUpdate" "${BUTTON_1}"
fi
if [ $? -eq 2 ]; then
log_write "warning - softwareupdate cancelled by user - exiting"
postponed=$((postponed+1))
pref_writelocal "${PREFERENCESDOMAIN_KEY_POSTPONED}" "-int" "${postponed}"
cleanup
exit 1
fi
else
guiText="${TEXT_2}"
fi
gui_hud_show "${guiText}" "SoftwareUpdate" "lr"
fi
log_write "installing software updates $(echo "${softwareUpdateList}" | tr "^\n" " ,") (backgrounded, timeout ${TIMEOUT_SOFTWAREUPDATEINSTALL} sec)"
softwareupdate_install_inbackground
waitedTime=$(softwareupdate_waitforcompletion ${TIMEOUT_SOFTWAREUPDATEINSTALL})
errl=$?
if [ ${errl} -eq 0 ] || [ ${waitedTime} -gt 0 ] ; then
log_write "software update completed in $((TIMEOUT_SOFTWAREUPDATEINSTALL-waitedTime+1)) seconds"
currentdate="$(date_get)"
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTSUCCESSFULLDATE}" "-string" "${currentdate}"
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTRUNDATE}" "-string" "${currentdate}"
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTRESULT}" "-string" "success"
gui_hud_close
softwareupdate_restart_check
if [ $? -eq 0 ]; then
# show gui only, if a user is logged in
user_loggedin_check
if [ $? -eq 0 ]; then
log_write "A user is logged in - show GUI"
gui_dialog_show "${TEXT_1_2}" "SoftwareUpdate" "${BUTTON_3}"
gui_hud_show "${TEXT_4}" "SoftwareUpdate"
fi
pref_writelocal "${PREFERENCESDOMAIN_KEY_POSTPONED}" "-int" "0"
cleanup
reboot_do "${RESTART_IMMEDIATE}"
fi
else
gui_hud_close
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTRUNDATE}" "-string" "$(date_get)"
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTRESULT}" "-string" "failed"
if [ ${waitedTime} -le 0 ]; then
softwareupdate_kill_backgroudprocess
log_write "error - software update could not complete in ${TIMEOUT_SOFTWAREUPDATEINSTALL} seconds - exiting"
## kill softwareupdate???
else
log_write "error - could not install softwareupdates"
fi
cleanup
exit 1
fi
else
pref_writelocal "${PREFERENCESDOMAIN_KEY_LASTRUNDATE}" "-string" "$(date_get)"
log_write "no Updates to install"
fi
cleanup
exit 0