#!/bin/bash
# The next line disables specific ShellCheck codes for the entire script.
# https://github.com/koalaman/shellcheck
# shellcheck disable=SC2001,SC2009,SC2207,SC2024
# S.U.P.E.R.M.A.N.
# Software Update/Upgrade Policy Enforcement (with) Recursive Messaging And Notification
# https://github.com/Macjutsu/super
# by Kevin M. White
superVERSION="3.0"
superDATE="2023/06/01"
# MARK: *** Documentation ***
################################################################################
# Show usage documentation.
showUsage() {
echo "
S.U.P.E.R.M.A.N.
Software Update Policy Enforcement (with) Recursive Messaging And Notification
Version $superVERSION
$superDATE
https://github.com/Macjutsu/super
Usage:
sudo ./super
Deferment Timer Options:
[--default-defer=seconds] [--focus-defer=seconds]
[--menu-defer=seconds,seconds,etc...] [--error-defer=seconds]
[--recheck-defer=seconds] [--delete-deferrals]
Deferment Count Deadline Options:
[--focus-count=number] [--soft-count=number] [--hard-count=number]
[--restart-counts] [--delete-counts]
Deferment Days Deadline Options:
[--focus-days=number] [--soft-days=number] [--hard-days=number]
[--zero-day=YYYY-MM-DD:hh:mm] [--restart-days] [--delete-days]
Deferment Date Deadline Options:
[--focus-date=YYYY-MM-DD:hh:mm] [--soft-date=YYYY-MM-DD:hh:mm]
[--hard-date=YYYY-MM-DD:hh:mm] [--delete-dates]
User Interface Options:
[--defer-dialog-timeout=seconds] [--soft-dialog-timeout=seconds]
[--display-redraw=seconds] [--display-icon=/local/path or URL]
[--icon-size-ibm=pixels] [--icon-size-jamf=pixels ]
[--display-accessory-type=TEXTBOX|HTMLBOX|HTML|IMAGE|VIDEO|VIDEOAUTO]
[--display-accessory-default=/local/path or URL]
[--display-accessory-update=/local/path or URL]
[--display-accessory-upgrade=/local/path or URL]
[--display-accessory-user-auth=/local/path or URL]
[--help-button=plain text or URL] [--warning-button=plain text or URL]
[--display-silently] [--display-silently-off]
[--prefer-jamf-helper] [--prefer-jamf-helper-off]
Apple Silicon Credential Options:
[--local-account=AccountName] [--local-password=Password]
[--admin-account=AccountName] [--admin-password=Password]
[--super-account=AccountName] [--super-password=Password]
[--jamf-account=AccountName] [--jamf-password=Password]
[--delete-accounts] [--user-auth-timeout=seconds]
[--user-auth-mdm-failover=ALWAYS,NOSERVICE,SOFT,HARD,INSTALLNOW,BOOTSTRAP]
Update, Upgrade, and Restart Options:
[--allow-upgrade] [--allow-upgrade-off] [--target-upgrade=version]
[--allow-rsr-updates] [--allow-rsr-updates-off]
[--enforce-non-system-updates] [--enforce-non-system-updates-off]
[--only-download] [--only-download-off]
[--install-now] [--install-now-off]
[--policy-triggers=PolicyTrigger,PolicyTrigger,etc...]
[--skip-updates] [--skip-updates-off]
[--restart-without-updates] [--restart-without-updates-off]
macOS Update/Upgrade Validation Options:
[--free-space-update=gigabytes] [--free-space-upgrade=gigabytes]
[--free-space-timeout=seconds] [--battery-level=percentage]
[--battery-timeout=seconds]
Testing, Validation, and Documentation:
[--test-mode] [--test-mode-off] [--test-mode-timeout=seconds]
[--verbose-mode] [--verbose-mode-off] [--open-logs] [--reset-super]
[--usage] [--help]
* Managed preferences override local options via domain: com.macjutsu.super
DefaultDefer seconds
FocusDefer seconds
MenuDefer seconds,seconds,etc...
RecheckDefer seconds
ErrorDefer seconds
FocusCount number
SoftCount number
HardCount number
FocusDays number
SoftDays number
HardDays number
ZeroDay YYYY-MM-DD:hh:mm
FocusDate YYYY-MM-DD:hh:mm
SoftDate YYYY-MM-DD:hh:mm
HardDate YYYY-MM-DD:hh:mm
DeferDialogTimeout seconds
SoftDialogTimeout seconds
DisplayRedraw seconds
DisplayIcon path
IconSizeIbm pixels
IconSizeJamf pixels
DisplayAccessoryType
TEXTBOX|HTMLBOX|HTML|IMAGE|VIDEO|VIDEOAUTO
DisplayAccessoryDefault path or URL
DisplayAccessoryUpdate path or URL
DisplayAccessoryUpgrade path or URL
DisplayAccessoryUserAuth path or URL
HelpButton plain text or URL
WarningButton plain text or URL
DisplaySilently |
PreferJamfHelper |
UserAuthTimeout seconds
UserAuthMDMFailover
ALWAYS,NOSERVICE,SOFT,HARD,INSTALLNOW,BOOTSTRAP
AllowUpgrade |
TargetUpgrade version
AllowRSRUpdates |
EnforceNonSystemUpdates |
OnlyDownload |
InstallNow |
PolicyTriggers PolicyTrigger,PolicyTrigger,etc...
SkipUpdates |
RestartWithoutUpdates |
FreeSpaceUpdate gigabytes
FreeSpaceUpgrade gigabytes
FreeSpaceTimeout seconds
BatteryLevel percentage
BatteryTimeout seconds
TestMode |
TestModeTimeout seconds
VerboseMode |
** For detailed documentation visit: https://github.com/Macjutsu/super/wiki
** Or use --help to automatically open the S.U.P.E.R.M.A.N. Wiki.
"
# Error log any unrecognized options.
if [[ -n ${unrecognizedOptionsARRAY[*]} ]]; then
sendToLog "Error: Unrecognized Options: ${unrecognizedOptionsARRAY[*]}"; parameterERROR="TRUE"
[[ "$jamfPARENT" == "TRUE" ]] && sendToLog "Error: Note that each Jamf Pro Policy Parameter can only contain a single option."
sendToStatus "Inactive Error: Unrecognized Options: ${unrecognizedOptionsARRAY[*]}"
fi
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION USAGE EXIT ****"
exit 0
}
# If there is a real current user then open the S.U.P.E.R.M.A.N. Wiki, otherwise run the showUsage() function.
showHelp() {
checkCurrentUser
if [[ "$currentUserNAME" != "FALSE" ]]; then
sendToLog "Startup: Opening S.U.P.E.R.M.A.N. Wiki for user \"$currentUserNAME\"."
sudo -u "$currentUserNAME" open "https://github.com/Macjutsu/super/wiki" &
else
showUsage
fi
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION HELP EXIT ****"
exit 0
}
# MARK: *** Parameters ***
################################################################################
# Set default parameters that are used throughout the script.
setDefaults() {
# Installation folder:
superFOLDER="/Library/Management/super"
# Symbolic link in default path for super.
superLINK="/usr/local/bin/super"
# Path to a PID file:
superPIDFILE="/var/run/super.pid"
# Path to a local property list file:
superPLIST="$superFOLDER/com.macjutsu.super" # No trailing ".plist"
# Path to a managed property list file:
superMANAGEDPLIST="/Library/Managed Preferences/com.macjutsu.super" # No trailing ".plist"
# Path to the log for the main super workflow:
superLOG="$superFOLDER/super.log"
# Path to the log for the current softwareupdate --list command result:
asuListLOG="$superFOLDER/asuList.log"
# Path to the log for the current erase-install.sh --list command result:
installerListLOG="$superFOLDER/installerList.log"
# Path to the log for all softwareupdate download/install workflows:
asuLOG="$superFOLDER/asu.log"
# Path to the log for all macOS installer application download/install workflows:
installerLOG="$superFOLDER/installer.log"
# Path to the log for filtered MDM client command progress:
mdmCommandLOG="$superFOLDER/mdmCommand.log"
# Path to the log for debug MDM client command progress:
mdmCommandDebugLOG="$superFOLDER/mdmCommandDebug.log"
# Path to the log for filtered MDM update/upgrade workflow progress:
mdmWorkflowLOG="$superFOLDER/mdmWorkflow.log"
# Path to the log for debug MDM update/upgrade workflow progress:
mdmWorkflowDebugLOG="$superFOLDER/mdmWorkflowDebug.log"
# Path to the "hidden" file that triggers a macOS update/upgrade restart validation workflow:
restartValidateFilePATH="$superFOLDER/.RestartValidate"
# This is the name for the LaunchDaemon.
launchDaemonNAME="com.macjutsu.super" # No trailing ".plist"
# Path to the jamf binary:
jamfBINARY="/usr/local/bin/jamf"
# Path to the jamfHELPER binary:
jamfHELPER="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
# URL to the IBM Notifier.app download:
ibmNotifierURL="https://github.com/IBM/mac-ibm-notifications/releases/download/v-2.9.1-b-96/IBM.Notifier.zip"
# Target version for IBM Notifier.app:
ibmNotifierVERSION="2.9.1"
# Path to the local IBM Notifier.app:
ibmNotifierAPP="$superFOLDER/IBM Notifier.app"
# Path to the local IBM Notifier.app binary:
ibmNotifierBINARY="$ibmNotifierAPP/Contents/MacOS/IBM Notifier"
# URL to the erase-install package installer:
eraseInstallURL="https://github.com/grahampugh/erase-install/releases/download/v27.3/erase-install-27.3.pkg"
# Target version for erase-install.sh:
eraseInstallVERSION="27.3"
# Target checksum for erase-install.sh:
eraseInstallCHECKSUM="890f3ec8fe0e2efa7b33d407eee96358d8a44ca4"
# Path to the local erase-install folder:
eraseInstallFOLDER="/Library/Management/erase-install"
# IMPORTANT DETAIL: super does NOT move the default erase-install folder content to another custom location.
# Changing this folder path to anything besides "/Library/Management/erase-install" requires that you must also deploy the erase-install folder to the custom location prior to using super.
# Path to the local copy of erase-install.sh:
eraseInstallSCRIPT="$eraseInstallFOLDER/erase-install.sh"
# Path to the local copy of installinstallmacOS.py:
installInstallMacOS="$eraseInstallFOLDER/installinstallmacOS.py"
# Path to the local copy of movable Python.framework:
pythonFRAMEWORK="$eraseInstallFOLDER/Python.framework"
# Path to a local softwareupdate property list file:
asuPLIST="/Library/Preferences/com.apple.SoftwareUpdate" # No trailing ".plist"
# Path to for the local cached display icon:
cachedICON="$superFOLDER/icon.png"
# The default icon in the if no $displayIconOPTION is specified or found.
defaultICON="/System/Library/PrivateFrameworks/SoftwareUpdate.framework/Versions/A/Resources/SoftwareUpdate.icns"
# Default icon size for IBM Notifier.app.
ibmNotifierIconSIZE=96
# Default icon size for jamfHelper.
jamfHelperIconSIZE=96
# Deadline date display format.
dateFORMAT="+%B %d, %Y" # Formatting options can be found in the man page for the date command.
# Deadline time display format.
timeFORMAT="+%l:%M %p" # Formatting options can be found in the man page for the date command.
# The default number of seconds to defer if a user choses not to restart now.
defaultDeferSECONDS=3600
# The default number of seconds to defer if there is a workflow error.
errorDeferSECONDS=3600
# The default user authentication dialog timeout.
userAuthTimeoutSECONDS=3600
# The default minium free storage space in gigabytes required for a macOS update.
freeSpaceUpdateGB=15
# The default minium free storage space in gigabytes required for a macOS upgrade.
freeSpaceUpgradeGB=35
# The default macOS upgrade installer estimated size (macOS update sizes are automatically collected via softwareupdate).
macOSInstallerGB=13
# The number of seconds between storage checks when displaying the insufficient free space notification via the notifyStorage() function.
storageRecheckSECONDS=5
# The default insufficient available free space notification timeout.
freeSpaceTimeoutSECONDS=3600
# The default battery level percentage required for a macOS software update/upgrade.
batteryLevelPERCENT=50
# The number of seconds between AC power checks when displaying the insufficient battery notification via the notifyStorage() function.
powerRecheckSECONDS=1
# The default AC Power required for low battery notification timeout.
batteryTimeoutSECONDS=3600
# The number of seconds to timeout various workflow startup processes if no progress is reported.
initialStartTimeoutSECONDS=120
# The number of seconds to timeout the macOS 11+ softwareupdate download/prepare workflow if no progress is reported.
softwareUpdateTimeoutSECONDS=1200
# The number of seconds to timeout the macOS 10.x softwareupdate download/prepare workflow if no progress is reported.
softwareUpdateLegacyTimeoutSECONDS=3600
# The number of seconds to timeout the softwareupdate recommended (non-system) update workflow if no progress is reported.
softwareUpdateRecommendedTimeoutSECONDS=600
# The number of seconds to timeout the macOS installer download workflow if no progress is reported.
macOSInstallerDownloadTimeoutSECONDS=300
# The number of seconds to timeout the macOS installation workflow if no progress is reported.
macOSInstallerTimeoutSECONDS=600
# The number of seconds to timeout MDM commands if no response is reported.
mdmTimeoutSECONDS=300
# The number of seconds to timeout the MDM download/prepare workflow if no progress is reported.
mdmWorkflowTimeoutSECONDS=600
# The default amount of time in seconds to leave test notifications and dialogs open before moving on with the test mode workflow.
testModeTimeoutSECONDS=10
# These parameters identify the relevant system information.
macOSMAJOR=$(sw_vers -productVersion | cut -d'.' -f1) # Expected output: 10, 11, 12
macOSMINOR=$(sw_vers -productVersion | cut -d'.' -f2) # Expected output: 14, 15, 06, 01
macOSVERSION=${macOSMAJOR}$(printf "%02d" "$macOSMINOR") # Expected output: 1014, 1015, 1106, 1203
[[ "$macOSMAJOR" -ge 13 ]] && macOSEXTRA=$(sw_vers -productVersionExtra | cut -d'.' -f2) # Expected output: (a), (b), (c)
macOSBUILD=$(sw_vers -buildVersion) # Expected output: 22D68
macOSNAME="macOS $(awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'macOS ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}')" # Expected output: macOS Ventura
macOSARCH=$(arch) # Expected output: i386, arm64
macModelID="$(system_profiler SPHardwareDataType | grep 'Model Identifier' | awk -F ': ' '{print $2}')" # Expected output: MacBookPro18,2
[[ $(echo "$macModelID" | grep -c 'Book') -gt 0 ]] && macBOOK="TRUE" # Expected output: TRUE
lastREBOOT="$(last reboot | head -1 | sed -e 's/reboot ~ //' | xargs)" # Expected output: Sat Feb 18 11:45
}
# Collect input options and set associated parameters.
getOptions() {
# If super is running via Jamf Policy installation then the first 3 input parameters are skipped.
if [[ $1 == "/" ]]; then
shift 3
jamfPARENT="TRUE"
fi
# getOptions debug mode.
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: @ is:\n$@"
# This is a standard while/case loop to collect all the input parameters.
while [[ -n $1 ]]; do
case "$1" in
--default-defer* )
defaultDeferOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--focus-defer* )
focusDeferOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--menu-defer* )
menuDeferOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--recheck-defer* )
recheckDeferOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--error-defer* )
errorDeferOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--delete-deferrals )
deleteDEFFERALS="TRUE"
;;
--focus-count* )
focusCountOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--soft-count* )
softCountOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--hard-count* )
hardCountOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--restart-counts )
restartCOUNTS="TRUE"
;;
--delete-counts )
deleteCOUNTS="TRUE"
;;
--focus-days* )
focusDaysOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--soft-days* )
softDaysOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--hard-days* )
hardDaysOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--zero-day* )
zeroDayOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--restart-days )
restartDAYS="TRUE"
;;
--delete-days )
deleteDAYS="TRUE"
;;
--focus-date* )
focusDateOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--soft-date* )
softDateOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--hard-date* )
hardDateOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--delete-dates )
deleteDATES="TRUE"
;;
--defer-dialog-timeout* )
deferDialogTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--soft-dialog-timeout* )
softDialogTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-redraw* )
displayRedrawOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-icon* )
displayIconOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--icon-size-ibm* )
iconSizeIbmOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--icon-size-jamf* )
iconSizeJamfOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-accessory-type* )
displayAccessoryTypeOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-accessory-default* )
displayAccessoryDefaultOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-accessory-update* )
displayAccessoryUpdateOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-accessory-upgrade* )
displayAccessoryUpgradeOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--display-accessory-user-auth* )
displayAccessoryUserAuthOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--help-button* )
helpButtonOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--warning-button* )
warningButtonOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-Q|--display-silently|--display-silently-on )
displaySilentlyOPTION="TRUE"
;;
-q|--display-silently-off|--no-display-silently )
displaySilentlyOPTION="FALSE"
;;
-J|--prefer-jamf-helper|--prefer-jamf-helper-on )
preferJamfHelperOPTION="TRUE"
;;
-j|--prefer-jamf-helper-off|--no-prefer-jamf-helper )
preferJamfHelperOPTION="FALSE"
;;
--local-account* )
localOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--local-password* )
localPASSWORD=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--admin-account* )
adminACCOUNT=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--admin-password* )
adminPASSWORD=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--super-account* )
superOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--super-password* )
superPASSWORD=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--jamf-account* )
jamfOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--jamf-password* )
jamfPASSWORD=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-d|-D|--delete-accounts )
deleteACCOUNTS="TRUE"
;;
-M|--allow-upgrade|--allow-upgrade-on )
allowUpgradeOPTION="TRUE"
;;
-m|--allow-upgrade-off|--no-allow-upgrade )
allowUpgradeOPTION="FALSE"
;;
--user-auth-timeout* )
userAuthTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--user-auth-mdm-failover* )
userAuthMDMFailoverOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--target-upgrade* )
targetUpgradeOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-N|--enforce-non-system-updates|--enforce-non-system-updates-on )
enforceNonSystemUpdatesOPTION="TRUE"
;;
-n|--enforce-non-system-updates-off|--no-enforce-non-system-updates )
enforceNonSystemUpdatesOPTION="FALSE"
;;
-R|--allow-rsr-updates|--allow-rsr-updates-on )
allowRSRUpdatesOPTION="TRUE"
;;
-r|--allow-rsr-updates-off|--no-allow-rsr-updates )
allowRSRUpdatesOPTION="FALSE"
;;
-O|--only-download|--only-download-on )
onlyDownloadOPTION="TRUE"
;;
-o|--only-download-off|--no-only-download )
onlyDownloadOPTION="FALSE"
;;
-I|--install-now|--install-now-on )
installNowOPTION="TRUE"
;;
-i|--install-now-off|--no-update-now )
installNowOPTION="FALSE"
;;
--policy-triggers* )
policyTriggersOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-S|--skip-updates|--skip-updates-on )
skipUpdatesOPTION="TRUE"
;;
-s|--skip-updates-off|--no-skip-updates )
skipUpdatesOPTION="FALSE"
;;
-W|--restart-without-updates|--restart-without-updates-on )
restartWithoutUpdatesOPTION="TRUE"
;;
-w|--restart-without-updates-off|--no-restart-without-updates )
restartWithoutUpdatesOPTION="FALSE"
;;
--free-space-update* )
freeSpaceUpdateOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--free-space-upgrade* )
freeSpaceUpgradeOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--free-space-timeout* )
freeSpaceTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--battery-level* )
batteryLevelOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
--battery-timeout* )
batteryTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-T|--test-mode|--test-mode-on )
testModeOPTION="TRUE"
;;
-t|--test-mode-off|--no-test-mode )
testModeOPTION="FALSE"
;;
--test-mode-timeout* )
testModeTimeoutOPTION=$(echo "$1" | sed -e 's|^[^=]*=||g')
;;
-V|--verbose-mode|--verbose-mode-on )
verboseModeOPTION="TRUE"
;;
-v|--verbose-mode-off|--no-verbose-mode )
verboseModeOPTION="FALSE"
;;
-l|-L|--open-logs )
openLOGS="TRUE"
;;
-x|-X|--reset-super )
resetLocalPROPERTIES="TRUE"
;;
-u|-U|--usage )
showUsage
;;
-h|-H|--help )
showHelp
;;
*)
unrecognizedOptionsARRAY+=("$1")
;;
esac
shift
done
# Error log any unrecognized options.
[[ -n ${unrecognizedOptionsARRAY[*]} ]] && showUsage
}
# Collect any parameters stored in $superMANAGEDPLIST and/or $superPLIST.
getPreferences() {
# If $deleteDEFFERALS is specified, then delete all local deferral preferences.
if [[ "$deleteDEFFERALS" == "TRUE" ]]; then
sendToLog "Startup: Deleting all local deferral preferences."
defaults delete "$superPLIST" DefaultDefer 2> /dev/null
defaults delete "$superPLIST" FocusDefer 2> /dev/null
defaults delete "$superPLIST" MenuDefer 2> /dev/null
defaults delete "$superPLIST" ErrorDefer 2> /dev/null
defaults delete "$superPLIST" RecheckDefer 2> /dev/null
fi
# If $deleteCOUNTS is specified, then delete all local maximum deferral count deadline preferences.
if [[ "$deleteCOUNTS" == "TRUE" ]]; then
sendToLog "Startup: Deleting all local maximum deferral count deadline preferences."
defaults delete "$superPLIST" FocusCount 2> /dev/null
defaults delete "$superPLIST" SoftCount 2> /dev/null
defaults delete "$superPLIST" HardCount 2> /dev/null
fi
# If $deleteDAYS is specified, then delete all local maximum day deadline preferences.
if [[ "$deleteDAYS" == "TRUE" ]]; then
sendToLog "Startup: Deleting all local maximum day deadline preferences."
defaults delete "$superPLIST" FocusDays 2> /dev/null
defaults delete "$superPLIST" SoftDays 2> /dev/null
defaults delete "$superPLIST" HardDays 2> /dev/null
defaults delete "$superPLIST" ZeroDay 2> /dev/null
fi
# If $deleteDATES is specified, then delete all local date deadline preferences.
if [[ "$deleteDATES" == "TRUE" ]]; then
sendToLog "Startup: Deleting all local date deadline preferences."
defaults delete "$superPLIST" FocusDate 2> /dev/null
defaults delete "$superPLIST" SoftDate 2> /dev/null
defaults delete "$superPLIST" HardDate 2> /dev/null
fi
# If $resetLocalPROPERTIES is specified, then delete all local non-account preferences.
if [[ "$resetLocalPROPERTIES" == "TRUE" ]]; then
sendToLog "Startup: Deleting all local non-account preferences."
defaults delete "$superPLIST" DefaultDefer 2> /dev/null
defaults delete "$superPLIST" FocusDefer 2> /dev/null
defaults delete "$superPLIST" MenuDefer 2> /dev/null
defaults delete "$superPLIST" RecheckDefer 2> /dev/null
defaults delete "$superPLIST" ErrorDefer 2> /dev/null
defaults delete "$superPLIST" FocusCount 2> /dev/null
defaults delete "$superPLIST" SoftCount 2> /dev/null
defaults delete "$superPLIST" HardCount 2> /dev/null
defaults delete "$superPLIST" FocusDays 2> /dev/null
defaults delete "$superPLIST" SoftDays 2> /dev/null
defaults delete "$superPLIST" HardDays 2> /dev/null
defaults delete "$superPLIST" ZeroDay 2> /dev/null
defaults delete "$superPLIST" FocusDate 2> /dev/null
defaults delete "$superPLIST" SoftDate 2> /dev/null
defaults delete "$superPLIST" HardDate 2> /dev/null
defaults delete "$superPLIST" DeferDialogTimeout 2> /dev/null
defaults delete "$superPLIST" SoftDialogTimeout 2> /dev/null
defaults delete "$superPLIST" DisplayRedraw 2> /dev/null
defaults delete "$superPLIST" DisplayIcon 2> /dev/null
rm -r "$cachedICON" > /dev/null 2>&1
defaults delete "$superPLIST" IconSizeIbm 2> /dev/null
defaults delete "$superPLIST" IconSizeJamf 2> /dev/null
defaults delete "$superPLIST" DisplayAccessoryType 2> /dev/null
defaults delete "$superPLIST" DisplayAccessoryDefault 2> /dev/null
defaults delete "$superPLIST" DisplayAccessoryUpdate 2> /dev/null
defaults delete "$superPLIST" DisplayAccessoryUpgrade 2> /dev/null
defaults delete "$superPLIST" DisplayAccessoryUserAuth 2> /dev/null
defaults delete "$superPLIST" HelpButton 2> /dev/null
defaults delete "$superPLIST" WarningButton 2> /dev/null
defaults delete "$superPLIST" DisplaySilently 2> /dev/null
defaults delete "$superPLIST" PreferJamfHelper 2> /dev/null
defaults delete "$superPLIST" UserAuthTimeout 2> /dev/null
defaults delete "$superPLIST" UserAuthMDMFailover 2> /dev/null
defaults delete "$superPLIST" AllowUpgrade 2> /dev/null
defaults delete "$superPLIST" TargetUpgrade 2> /dev/null
defaults delete "$superPLIST" AllowRSRUpdates 2> /dev/null
defaults delete "$superPLIST" EnforceNonSystemUpdates 2> /dev/null
defaults delete "$superPLIST" OnlyDownload 2> /dev/null
defaults delete "$superPLIST" InstallNow 2> /dev/null
defaults delete "$superPLIST" PolicyTriggers 2> /dev/null
defaults delete "$superPLIST" SkipUpdates 2> /dev/null
defaults delete "$superPLIST" RestartWithoutUpdates 2> /dev/null
defaults delete "$superPLIST" FreeSpaceUpdate 2> /dev/null
defaults delete "$superPLIST" FreeSpaceUpgrade 2> /dev/null
defaults delete "$superPLIST" FreeSpaceTimeout 2> /dev/null
defaults delete "$superPLIST" BatteryLevel 2> /dev/null
defaults delete "$superPLIST" BatteryTimeout 2> /dev/null
defaults delete "$superPLIST" TestMode 2> /dev/null
defaults delete "$superPLIST" TestModeTimeout 2> /dev/null
defaults delete "$superPLIST" VerboseMode 2> /dev/null
restartZeroDay
restartDeferralCounters
defaults delete "$superPLIST" SoftwareUpdatesList 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpgradeLabel 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpgradeTitle 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeName 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeVersion 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpdateDownloadLabel 2> /dev/null
defaults delete "$superPLIST" LastReboot 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
rm -f "$restartValidateFilePATH" 2> /dev/null
unset restartValidate
defaults delete "$superPLIST" RestartValidate 2> /dev/null # This line is only here to remove any legacy RestartValidate keys.
fullCheckREQUIRED="TRUE"
rm -f "$asuListLOG" 2> /dev/null
touch "$asuListLOG"
rm -f "$installerListLOG" 2> /dev/null
touch "$installerListLOG"
fi
# Collect any managed preferences from $superMANAGEDPLIST.
if [[ -f "$superMANAGEDPLIST.plist" ]]; then
jamfProIdMANAGED=$(defaults read "$superMANAGEDPLIST" JamfProID 2> /dev/null)
defaultDeferMANAGED=$(defaults read "$superMANAGEDPLIST" DefaultDefer 2> /dev/null)
focusDeferMANAGED=$(defaults read "$superMANAGEDPLIST" FocusDefer 2> /dev/null)
menuDeferMANAGED=$(defaults read "$superMANAGEDPLIST" MenuDefer 2> /dev/null)
recheckDeferMANAGED=$(defaults read "$superMANAGEDPLIST" RecheckDefer 2> /dev/null)
errorDeferMANAGED=$(defaults read "$superMANAGEDPLIST" ErrorDefer 2> /dev/null)
focusCountMANAGED=$(defaults read "$superMANAGEDPLIST" FocusCount 2> /dev/null)
softCountMANAGED=$(defaults read "$superMANAGEDPLIST" SoftCount 2> /dev/null)
hardCountMANAGED=$(defaults read "$superMANAGEDPLIST" HardCount 2> /dev/null)
focusDaysMANAGED=$(defaults read "$superMANAGEDPLIST" FocusDays 2> /dev/null)
softDaysMANAGED=$(defaults read "$superMANAGEDPLIST" SoftDays 2> /dev/null)
hardDaysMANAGED=$(defaults read "$superMANAGEDPLIST" HardDays 2> /dev/null)
zeroDayMANAGED=$(defaults read "$superMANAGEDPLIST" ZeroDay 2> /dev/null)
focusDateMANAGED=$(defaults read "$superMANAGEDPLIST" FocusDate 2> /dev/null)
softDateMANAGED=$(defaults read "$superMANAGEDPLIST" SoftDate 2> /dev/null)
hardDateMANAGED=$(defaults read "$superMANAGEDPLIST" HardDate 2> /dev/null)
deferDialogTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" DeferDialogTimeout 2> /dev/null)
softDialogTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" SoftDialogTimeout 2> /dev/null)
displayRedrawMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayRedraw 2> /dev/null)
displayIconMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayIcon 2> /dev/null)
iconSizeIbmMANAGED=$(defaults read "$superMANAGEDPLIST" IconSizeIbm 2> /dev/null)
iconSizeJamfMANAGED=$(defaults read "$superMANAGEDPLIST" IconSizeJamf 2> /dev/null)
displayAccessoryTypeMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayAccessoryType 2> /dev/null)
displayAccessoryDefaultMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayAccessoryDefault 2> /dev/null)
displayAccessoryUpdateMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayAccessoryUpdate 2> /dev/null)
displayAccessoryUpgradeMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayAccessoryUpgrade 2> /dev/null)
displayAccessoryUserAuthMANAGED=$(defaults read "$superMANAGEDPLIST" DisplayAccessoryUserAuth 2> /dev/null)
helpButtonMANAGED=$(defaults read "$superMANAGEDPLIST" HelpButton 2> /dev/null)
warningButtonMANAGED=$(defaults read "$superMANAGEDPLIST" WarningButton 2> /dev/null)
displaySilentlyMANAGED=$(defaults read "$superMANAGEDPLIST" DisplaySilently 2> /dev/null)
preferJamfHelperMANAGED=$(defaults read "$superMANAGEDPLIST" PreferJamfHelper 2> /dev/null)
userAuthTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" UserAuthTimeout 2> /dev/null)
userAuthMDMFailoverMANAGED=$(defaults read "$superMANAGEDPLIST" UserAuthMDMFailover 2> /dev/null)
allowUpgradeMANAGED=$(defaults read "$superMANAGEDPLIST" AllowUpgrade 2> /dev/null)
targetUpgradeMANAGED=$(defaults read "$superMANAGEDPLIST" TargetUpgrade 2> /dev/null)
allowRSRUpdatesMANAGED=$(defaults read "$superMANAGEDPLIST" AllowRSRUpdates 2> /dev/null)
enforceNonSystemUpdatesMANAGED=$(defaults read "$superMANAGEDPLIST" EnforceNonSystemUpdates 2> /dev/null)
onlyDownloadMANAGED=$(defaults read "$superMANAGEDPLIST" OnlyDownload 2> /dev/null)
installNowMANAGED=$(defaults read "$superMANAGEDPLIST" InstallNow 2> /dev/null)
policyTriggersMANAGED=$(defaults read "$superMANAGEDPLIST" PolicyTriggers 2> /dev/null)
skipUpdatesMANAGED=$(defaults read "$superMANAGEDPLIST" SkipUpdates 2> /dev/null)
restartWithoutUpdatesMANAGED=$(defaults read "$superMANAGEDPLIST" RestartWithoutUpdates 2> /dev/null)
freeSpaceUpdateMANAGED=$(defaults read "$superMANAGEDPLIST" FreeSpaceUpdate 2> /dev/null)
freeSpaceUpgradeMANAGED=$(defaults read "$superMANAGEDPLIST" FreeSpaceUpgrade 2> /dev/null)
freeSpaceTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" FreeSpaceTimeout 2> /dev/null)
batteryLevelMANAGED=$(defaults read "$superMANAGEDPLIST" BatteryLevel 2> /dev/null)
batteryTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" BatteryTimeout 2> /dev/null)
testModeMANAGED=$(defaults read "$superMANAGEDPLIST" TestMode 2> /dev/null)
testModeTimeoutMANAGED=$(defaults read "$superMANAGEDPLIST" TestModeTimeout 2> /dev/null)
verboseModeMANAGED=$(defaults read "$superMANAGEDPLIST" VerboseMode 2> /dev/null)
fi
# Collect any local preferences from $superPLIST.
if [[ -f "$superPLIST.plist" ]]; then
defaultDeferPROPERTY=$(defaults read "$superPLIST" DefaultDefer 2> /dev/null)
focusDeferPROPERTY=$(defaults read "$superPLIST" FocusDefer 2> /dev/null)
menuDeferPROPERTY=$(defaults read "$superPLIST" MenuDefer 2> /dev/null)
recheckDeferPROPERTY=$(defaults read "$superPLIST" RecheckDefer 2> /dev/null)
errorDeferPROPERTY=$(defaults read "$superPLIST" ErrorDefer 2> /dev/null)
focusCountPROPERTY=$(defaults read "$superPLIST" FocusCount 2> /dev/null)
softCountPROPERTY=$(defaults read "$superPLIST" SoftCount 2> /dev/null)
hardCountPROPERTY=$(defaults read "$superPLIST" HardCount 2> /dev/null)
focusDaysPROPERTY=$(defaults read "$superPLIST" FocusDays 2> /dev/null)
softDaysPROPERTY=$(defaults read "$superPLIST" SoftDays 2> /dev/null)
hardDaysPROPERTY=$(defaults read "$superPLIST" HardDays 2> /dev/null)
zeroDayPROPERTY=$(defaults read "$superPLIST" ZeroDay 2> /dev/null)
focusDatePROPERTY=$(defaults read "$superPLIST" FocusDate 2> /dev/null)
softDatePROPERTY=$(defaults read "$superPLIST" SoftDate 2> /dev/null)
hardDatePROPERTY=$(defaults read "$superPLIST" HardDate 2> /dev/null)
deferDialogTimeoutPROPERTY=$(defaults read "$superPLIST" DeferDialogTimeout 2> /dev/null)
softDialogTimeoutPROPERTY=$(defaults read "$superPLIST" SoftDialogTimeout 2> /dev/null)
displayRedrawPROPERTY=$(defaults read "$superPLIST" DisplayRedraw 2> /dev/null)
iconSizeIbmPROPERTY=$(defaults read "$superPLIST" IconSizeIbm 2> /dev/null)
iconSizeJamfPROPERTY=$(defaults read "$superPLIST" IconSizeJamf 2> /dev/null)
displayAccessoryTypePROPERTY=$(defaults read "$superPLIST" DisplayAccessoryType 2> /dev/null)
displayAccessoryDefaultPROPERTY=$(defaults read "$superPLIST" DisplayAccessoryDefault 2> /dev/null)
displayAccessoryUpdatePROPERTY=$(defaults read "$superPLIST" DisplayAccessoryUpdate 2> /dev/null)
displayAccessoryUpgradePROPERTY=$(defaults read "$superPLIST" DisplayAccessoryUpgrade 2> /dev/null)
displayAccessoryUserAuthPROPERTY=$(defaults read "$superPLIST" DisplayAccessoryUserAuth 2> /dev/null)
helpButtonPROPERTY=$(defaults read "$superPLIST" HelpButton 2> /dev/null)
warningButtonPROPERTY=$(defaults read "$superPLIST" WarningButton 2> /dev/null)
displaySilentlyPROPERTY=$(defaults read "$superPLIST" DisplaySilently 2> /dev/null)
preferJamfHelperPROPERTY=$(defaults read "$superPLIST" PreferJamfHelper 2> /dev/null)
userAuthTimeoutPROPERTY=$(defaults read "$superPLIST" UserAuthTimeout 2> /dev/null)
userAuthMDMFailoverPROPERTY=$(defaults read "$superPLIST" UserAuthMDMFailover 2> /dev/null)
allowUpgradePROPERTY=$(defaults read "$superPLIST" AllowUpgrade 2> /dev/null)
targetUpgradePROPERTY=$(defaults read "$superPLIST" TargetUpgrade 2> /dev/null)
allowRSRUpdatesPROPERTY=$(defaults read "$superPLIST" AllowRSRUpdates 2> /dev/null)
enforceNonSystemUpdatesPROPERTY=$(defaults read "$superPLIST" EnforceNonSystemUpdates 2> /dev/null)
onlyDownloadPROPERTY=$(defaults read "$superPLIST" OnlyDownload 2> /dev/null)
installNowPROPERTY=$(defaults read "$superPLIST" InstallNow 2> /dev/null)
policyTriggersPROPERTY=$(defaults read "$superPLIST" PolicyTriggers 2> /dev/null)
skipUpdatesPROPERTY=$(defaults read "$superPLIST" SkipUpdates 2> /dev/null)
restartWithoutUpdatesPROPERTY=$(defaults read "$superPLIST" RestartWithoutUpdates 2> /dev/null)
freeSpaceUpdatePROPERTY=$(defaults read "$superPLIST" FreeSpaceUpdate 2> /dev/null)
freeSpaceUpgradePROPERTY=$(defaults read "$superPLIST" FreeSpaceUpgrade 2> /dev/null)
freeSpaceTimeoutPROPERTY=$(defaults read "$superPLIST" FreeSpaceTimeout 2> /dev/null)
batteryLevelPROPERTY=$(defaults read "$superPLIST" BatteryLevel 2> /dev/null)
batteryTimeoutPROPERTY=$(defaults read "$superPLIST" BatteryTimeout 2> /dev/null)
testModePROPERTY=$(defaults read "$superPLIST" TestMode 2> /dev/null)
testModeTimeoutPROPERTY=$(defaults read "$superPLIST" TestModeTimeout 2> /dev/null)
verboseModePROPERTY=$(defaults read "$superPLIST" VerboseMode 2> /dev/null)
fi
# This logic ensures the priority order of managed preference overrides the new input option which overrides the saved local preference.
if [[ -n $defaultDeferMANAGED ]]; then
defaultDeferOPTION="$defaultDeferMANAGED"
elif [[ -z $defaultDeferOPTION ]] && [[ -n $defaultDeferPROPERTY ]]; then
defaultDeferOPTION="$defaultDeferPROPERTY"
fi
if [[ -n $focusDeferMANAGED ]]; then
focusDeferOPTION="$focusDeferMANAGED"
elif [[ -z $focusDeferOPTION ]] && [[ -n $focusDeferPROPERTY ]]; then
focusDeferOPTION="$focusDeferPROPERTY"
fi
if [[ -n $menuDeferMANAGED ]]; then
menuDeferOPTION="$menuDeferMANAGED"
elif [[ -z $menuDeferOPTION ]] && [[ -n $menuDeferPROPERTY ]]; then
menuDeferOPTION="$menuDeferPROPERTY"
fi
if [[ -n $recheckDeferMANAGED ]]; then
recheckDeferOPTION="$recheckDeferMANAGED"
elif [[ -z $recheckDeferOPTION ]] && [[ -n $recheckDeferPROPERTY ]]; then
recheckDeferOPTION="$recheckDeferPROPERTY"
fi
if [[ -n $errorDeferMANAGED ]]; then
errorDeferOPTION="$errorDeferMANAGED"
elif [[ -z $errorDeferOPTION ]] && [[ -n $errorDeferPROPERTY ]]; then
errorDeferOPTION="$errorDeferPROPERTY"
fi
if [[ -n $focusCountMANAGED ]]; then
focusCountOPTION="$focusCountMANAGED"
elif [[ -z $focusCountOPTION ]] && [[ -n $focusCountPROPERTY ]]; then
focusCountOPTION="$focusCountPROPERTY"
fi
if [[ -n $softCountMANAGED ]]; then
softCountOPTION="$softCountMANAGED"
elif [[ -z $softCountOPTION ]] && [[ -n $softCountPROPERTY ]]; then
softCountOPTION="$softCountPROPERTY"
fi
if [[ -n $hardCountMANAGED ]]; then
hardCountOPTION="$hardCountMANAGED"
elif [[ -z $hardCountOPTION ]] && [[ -n $hardCountPROPERTY ]]; then
hardCountOPTION="$hardCountPROPERTY"
fi
if [[ -n $focusDaysMANAGED ]]; then
focusDaysOPTION="$focusDaysMANAGED"
elif [[ -z $focusDaysOPTION ]] && [[ -n $focusDaysPROPERTY ]]; then
focusDaysOPTION="$focusDaysPROPERTY"
fi
if [[ -n $softDaysMANAGED ]]; then
softDaysOPTION="$softDaysMANAGED"
elif [[ -z $softDaysOPTION ]] && [[ -n $softDaysPROPERTY ]]; then
softDaysOPTION="$softDaysPROPERTY"
fi
if [[ -n $hardDaysMANAGED ]]; then
hardDaysOPTION="$hardDaysMANAGED"
elif [[ -z $hardDaysOPTION ]] && [[ -n $hardDaysPROPERTY ]]; then
hardDaysOPTION="$hardDaysPROPERTY"
fi
if [[ -n $zeroDayMANAGED ]]; then
zeroDayOPTION="$zeroDayMANAGED"
elif [[ -z $zeroDayOPTION ]] && [[ -n $zeroDayPROPERTY ]]; then
zeroDayOPTION="$zeroDayPROPERTY"
fi
if [[ -n $focusDateMANAGED ]]; then
focusDateOPTION="$focusDateMANAGED"
elif [[ -z $focusDateOPTION ]] && [[ -n $focusDatePROPERTY ]]; then
focusDateOPTION="$focusDatePROPERTY"
fi
if [[ -n $softDateMANAGED ]]; then
softDateOPTION="$softDateMANAGED"
elif [[ -z $softDateOPTION ]] && [[ -n $softDatePROPERTY ]]; then
softDateOPTION="$softDatePROPERTY"
fi
if [[ -n $hardDateMANAGED ]]; then
hardDateOPTION="$hardDateMANAGED"
elif [[ -z $hardDateOPTION ]] && [[ -n $hardDatePROPERTY ]]; then
hardDateOPTION="$hardDatePROPERTY"
fi
if [[ -n $deferDialogTimeoutMANAGED ]]; then
deferDialogTimeoutOPTION="$deferDialogTimeoutMANAGED"
elif [[ -z $deferDialogTimeoutOPTION ]] && [[ -n $deferDialogTimeoutPROPERTY ]]; then
deferDialogTimeoutOPTION="$deferDialogTimeoutPROPERTY"
fi
if [[ -n $softDialogTimeoutMANAGED ]]; then
softDialogTimeoutOPTION="$softDialogTimeoutMANAGED"
elif [[ -z $softDialogTimeoutOPTION ]] && [[ -n $softDialogTimeoutPROPERTY ]]; then
softDialogTimeoutOPTION="$softDialogTimeoutPROPERTY"
fi
if [[ -n $displayRedrawMANAGED ]]; then
displayRedrawOPTION="$displayRedrawMANAGED"
elif [[ -z $displayRedrawOPTION ]] && [[ -n $displayRedrawPROPERTY ]]; then
displayRedrawOPTION="$displayRedrawPROPERTY"
fi
[[ -n $displayIconMANAGED ]] && displayIconOPTION="$displayIconMANAGED"
if [[ -n $iconSizeIbmMANAGED ]]; then
iconSizeIbmOPTION="$iconSizeIbmMANAGED"
elif [[ -z $iconSizeIbmOPTION ]] && [[ -n $iconSizeIbmPROPERTY ]]; then
iconSizeIbmOPTION="$iconSizeIbmPROPERTY"
fi
if [[ -n $iconSizeJamfMANAGED ]]; then
iconSizeJamfOPTION="$iconSizeJamfMANAGED"
elif [[ -z $iconSizeJamfOPTION ]] && [[ -n $iconSizeJamfPROPERTY ]]; then
iconSizeJamfOPTION="$iconSizeJamfPROPERTY"
fi
if [[ -n $displayAccessoryTypeMANAGED ]]; then
displayAccessoryTypeOPTION="$displayAccessoryTypeMANAGED"
elif [[ -z $displayAccessoryTypeOPTION ]] && [[ -n $displayAccessoryTypePROPERTY ]]; then
displayAccessoryTypeOPTION="$displayAccessoryTypePROPERTY"
fi
if [[ -n $displayAccessoryDefaultMANAGED ]]; then
displayAccessoryDefaultOPTION="$displayAccessoryDefaultMANAGED"
elif [[ -z $displayAccessoryDefaultOPTION ]] && [[ -n $displayAccessoryDefaultPROPERTY ]]; then
displayAccessoryDefaultOPTION="$displayAccessoryDefaultPROPERTY"
fi
if [[ -n $displayAccessoryUpdateMANAGED ]]; then
displayAccessoryUpdateOPTION="$displayAccessoryUpdateMANAGED"
elif [[ -z $displayAccessoryUpdateOPTION ]] && [[ -n $displayAccessoryUpdatePROPERTY ]]; then
displayAccessoryUpdateOPTION="$displayAccessoryUpdatePROPERTY"
fi
if [[ -n $displayAccessoryUpgradeMANAGED ]]; then
displayAccessoryUpgradeOPTION="$displayAccessoryUpgradeMANAGED"
elif [[ -z $displayAccessoryUpgradeOPTION ]] && [[ -n $displayAccessoryUpgradePROPERTY ]]; then
displayAccessoryUpgradeOPTION="$displayAccessoryUpgradePROPERTY"
fi
if [[ -n $displayAccessoryUserAuthMANAGED ]]; then
displayAccessoryUserAuthOPTION="$displayAccessoryUserAuthMANAGED"
elif [[ -z $displayAccessoryUserAuthOPTION ]] && [[ -n $displayAccessoryUserAuthPROPERTY ]]; then
displayAccessoryUserAuthOPTION="$displayAccessoryUserAuthPROPERTY"
fi
if [[ -n $helpButtonMANAGED ]]; then
helpButtonOPTION="$helpButtonMANAGED"
elif [[ -z $helpButtonOPTION ]] && [[ -n $helpButtonPROPERTY ]]; then
helpButtonOPTION="$helpButtonPROPERTY"
fi
if [[ -n $warningButtonMANAGED ]]; then
warningButtonOPTION="$warningButtonMANAGED"
elif [[ -z $warningButtonOPTION ]] && [[ -n $warningButtonPROPERTY ]]; then
warningButtonOPTION="$warningButtonPROPERTY"
fi
if [[ -n $displaySilentlyMANAGED ]]; then
displaySilentlyOPTION="$displaySilentlyMANAGED"
elif [[ -z $displaySilentlyOPTION ]] && [[ -n $displaySilentlyPROPERTY ]]; then
displaySilentlyOPTION="$displaySilentlyPROPERTY"
fi
if [[ -n $preferJamfHelperMANAGED ]]; then
preferJamfHelperOPTION="$preferJamfHelperMANAGED"
elif [[ -z $preferJamfHelperOPTION ]] && [[ -n $preferJamfHelperPROPERTY ]]; then
preferJamfHelperOPTION="$preferJamfHelperPROPERTY"
fi
if [[ -n $userAuthTimeoutMANAGED ]]; then
userAuthTimeoutOPTION="$userAuthTimeoutMANAGED"
elif [[ -z $userAuthTimeoutOPTION ]] && [[ -n $userAuthTimeoutPROPERTY ]]; then
userAuthTimeoutOPTION="$userAuthTimeoutPROPERTY"
fi
if [[ -n $userAuthMDMFailoverMANAGED ]]; then
userAuthMDMFailoverOPTION="$userAuthMDMFailoverMANAGED"
elif [[ -z $userAuthMDMFailoverOPTION ]] && [[ -n $userAuthMDMFailoverPROPERTY ]]; then
userAuthMDMFailoverOPTION="$userAuthMDMFailoverPROPERTY"
fi
if [[ -n $allowUpgradeMANAGED ]]; then
allowUpgradeOPTION="$allowUpgradeMANAGED"
elif [[ -z $allowUpgradeOPTION ]] && [[ -n $allowUpgradePROPERTY ]]; then
allowUpgradeOPTION="$allowUpgradePROPERTY"
fi
if [[ -n $targetUpgradeMANAGED ]]; then
targetUpgradeOPTION="$targetUpgradeMANAGED"
elif [[ -z $targetUpgradeOPTION ]] && [[ -n $targetUpgradePROPERTY ]]; then
targetUpgradeOPTION="$targetUpgradePROPERTY"
fi
if [[ -n $allowRSRUpdatesMANAGED ]]; then
allowRSRUpdatesOPTION="$allowRSRUpdatesMANAGED"
elif [[ -z $allowRSRUpdatesOPTION ]] && [[ -n $allowRSRUpdatesPROPERTY ]]; then
allowRSRUpdatesOPTION="$allowRSRUpdatesPROPERTY"
fi
if [[ -n $enforceNonSystemUpdatesMANAGED ]]; then
enforceNonSystemUpdatesOPTION="$enforceNonSystemUpdatesMANAGED"
elif [[ -z $enforceNonSystemUpdatesOPTION ]] && [[ -n $enforceNonSystemUpdatesPROPERTY ]]; then
enforceNonSystemUpdatesOPTION="$enforceNonSystemUpdatesPROPERTY"
fi
if [[ -n $onlyDownloadMANAGED ]]; then
onlyDownloadOPTION="$onlyDownloadMANAGED"
elif [[ -z $onlyDownloadOPTION ]] && [[ -n $onlyDownloadPROPERTY ]]; then
onlyDownloadOPTION="$onlyDownloadPROPERTY"
fi
if [[ -n $installNowMANAGED ]]; then
installNowOPTION="$installNowMANAGED"
elif [[ -z $installNowOPTION ]] && [[ -n $installNowPROPERTY ]]; then
installNowOPTION="$installNowPROPERTY"
fi
if [[ -n $policyTriggersMANAGED ]]; then
policyTriggersOPTION="$policyTriggersMANAGED"
elif [[ -z $policyTriggersOPTION ]] && [[ -n $policyTriggersPROPERTY ]]; then
policyTriggersOPTION="$policyTriggersPROPERTY"
fi
if [[ -n $skipUpdatesMANAGED ]]; then
skipUpdatesOPTION="$skipUpdatesMANAGED"
elif [[ -z $skipUpdatesOPTION ]] && [[ -n $skipUpdatesPROPERTY ]]; then
skipUpdatesOPTION="$skipUpdatesPROPERTY"
fi
if [[ -n $restartWithoutUpdatesMANAGED ]]; then
restartWithoutUpdatesOPTION="$restartWithoutUpdatesMANAGED"
elif [[ -z $restartWithoutUpdatesOPTION ]] && [[ -n $restartWithoutUpdatesPROPERTY ]]; then
restartWithoutUpdatesOPTION="$restartWithoutUpdatesPROPERTY"
fi
if [[ -n $freeSpaceUpdateMANAGED ]]; then
freeSpaceUpdateOPTION="$freeSpaceUpdateMANAGED"
elif [[ -z $freeSpaceUpdateOPTION ]] && [[ -n $freeSpaceUpdatePROPERTY ]]; then
freeSpaceUpdateOPTION="$freeSpaceUpdatePROPERTY"
fi
if [[ -n $freeSpaceUpgradeMANAGED ]]; then
freeSpaceUpgradeOPTION="$freeSpaceUpgradeMANAGED"
elif [[ -z $freeSpaceUpgradeOPTION ]] && [[ -n $freeSpaceUpgradePROPERTY ]]; then
freeSpaceUpgradeOPTION="$freeSpaceUpgradePROPERTY"
fi
if [[ -n $freeSpaceTimeoutMANAGED ]]; then
freeSpaceTimeoutOPTION="$freeSpaceTimeoutMANAGED"
elif [[ -z $freeSpaceTimeoutOPTION ]] && [[ -n $freeSpaceTimeoutPROPERTY ]]; then
freeSpaceTimeoutOPTION="$freeSpaceTimeoutPROPERTY"
fi
if [[ -n $batteryLevelMANAGED ]]; then
batteryLevelOPTION="$batteryLevelMANAGED"
elif [[ -z $batteryLevelOPTION ]] && [[ -n $batteryLevelPROPERTY ]]; then
batteryLevelOPTION="$batteryLevelPROPERTY"
fi
if [[ -n $batteryTimeoutMANAGED ]]; then
batteryTimeoutOPTION="$batteryTimeoutMANAGED"
elif [[ -z $batteryTimeoutOPTION ]] && [[ -n $batteryTimeoutPROPERTY ]]; then
batteryTimeoutOPTION="$batteryTimeoutPROPERTY"
fi
if [[ -n $testModeMANAGED ]]; then
testModeOPTION="$testModeMANAGED"
elif [[ -z $testModeOPTION ]] && [[ -n $testModePROPERTY ]]; then
testModeOPTION="$testModePROPERTY"
fi
if [[ -n $testModeTimeoutMANAGED ]]; then
testModeTimeoutOPTION="$testModeTimeoutMANAGED"
elif [[ -z $testModeTimeoutOPTION ]] && [[ -n $testModeTimeoutPROPERTY ]]; then
testModeTimeoutOPTION="$testModeTimeoutPROPERTY"
fi
if [[ -n $verboseModeMANAGED ]]; then
verboseModeOPTION="$verboseModeMANAGED"
elif [[ -z $verboseModeOPTION ]] && [[ -n $verboseModePROPERTY ]]; then
verboseModeOPTION="$verboseModePROPERTY"
fi
}
# Validate non-credential parameters and manage $superPLIST. Any errors set $parameterERROR.
manageParameters() {
parameterERROR="FALSE"
# Various regular expressions used for parameter validation.
regexNUMBER="^[0-9]+$"
regexMENU="^[0-9*,]+$"
regexDATE="^[0-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$"
regexTIME="^(2[0-3]|[01][0-9]):[0-5][0-9]$"
regexDATETIME="^[0-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]):(2[0-3]|[01][0-9]):[0-5][0-9]$"
regexMACOSMAJORVERSION="^([1][1-9])$"
# Validate $defaultDeferOPTION input and if valid override default $defaultDeferSECONDS parameter and save to $superPLIST.
if [[ "$defaultDeferOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for default deferral."
defaults delete "$superPLIST" DefaultDefer 2> /dev/null
elif [[ -n $defaultDeferOPTION ]] && [[ $defaultDeferOPTION =~ $regexNUMBER ]]; then
if [[ $defaultDeferOPTION -lt 120 ]]; then
sendToLog "Warning: Specified default deferral time of $defaultDeferOPTION seconds is too low, rounding up to 120 seconds."
defaultDeferSECONDS=120
elif [[ $defaultDeferOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified default deferral time of $defaultDeferOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
defaultDeferSECONDS=86400
else
defaultDeferSECONDS=$defaultDeferOPTION
fi
defaults write "$superPLIST" DefaultDefer -string "$defaultDeferSECONDS"
elif [[ -n $defaultDeferOPTION ]] && ! [[ $defaultDeferOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The default deferral time must only be a number."; parameterERROR="TRUE"
fi
# Validate $focusDeferOPTION input and if valid set $focusDeferSECONDS and save to $superPLIST.
if [[ "$focusDeferOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for Focus deferral."
defaults delete "$superPLIST" FocusDefer 2> /dev/null
elif [[ -n $focusDeferOPTION ]] && [[ $focusDeferOPTION =~ $regexNUMBER ]]; then
if [[ $focusDeferOPTION -lt 120 ]]; then
sendToLog "Warning: Specified focus deferral time of $focusDeferOPTION seconds is too low, rounding up to 120 seconds."
focusDeferSECONDS=120
elif [[ $focusDeferOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified focus deferral time of $focusDeferOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
focusDeferSECONDS=86400
else
focusDeferSECONDS=$focusDeferOPTION
fi
defaults write "$superPLIST" FocusDefer -string "$focusDeferSECONDS"
elif [[ -n $focusDeferOPTION ]] && ! [[ $focusDeferOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The focus deferral time must only be a number."; parameterERROR="TRUE"
fi
# Validate $menuDeferOPTION input and if valid set $menuDeferSECONDS and save to $superPLIST.
if [[ "$menuDeferOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for menu deferral."
defaults delete "$superPLIST" MenuDefer 2> /dev/null
elif [[ -n $menuDeferOPTION ]] && [[ $menuDeferOPTION =~ $regexMENU ]]; then
oldIFS="$IFS"; IFS=','
read -r -a menuDeferARRAY <<< "$menuDeferOPTION"
for i in "${!menuDeferARRAY[@]}"; do
if [[ ${menuDeferARRAY[i]} -lt 120 ]]; then
sendToLog "Warning: Specified menu deferral time of ${menuDeferARRAY[i]} seconds is too low, rounding up to 120 seconds."
menuDeferARRAY[i]=120
elif [[ ${menuDeferARRAY[i]} -gt 86400 ]]; then
sendToLog "Warning: Specified menu deferral time of ${menuDeferARRAY[i]} seconds is too high, rounding down to 86400 seconds (1 day)."
menuDeferARRAY[i]=86400
fi
done
menuDeferSECONDS="${menuDeferARRAY[*]}"
defaults write "$superPLIST" MenuDefer -string "$menuDeferSECONDS"
IFS="$oldIFS"
elif [[ -n $menuDeferOPTION ]] && ! [[ $menuDeferOPTION =~ $regexMENU ]]; then
sendToLog "Parameter Error: The defer pop-up menu time(s) must only contain numbers and commas (no spaces)."; parameterERROR="TRUE"
fi
# Validate $recheckDeferOPTION input and if valid set $recheckDeferSECONDS and save to $superPLIST.
if [[ "$recheckDeferOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for recheck deferral."
defaults delete "$superPLIST" RecheckDefer 2> /dev/null
elif [[ -n $recheckDeferOPTION ]] && [[ $recheckDeferOPTION =~ $regexNUMBER ]]; then
if [[ $recheckDeferOPTION -lt 120 ]]; then
sendToLog "Warning: Specified recheck deferral time of $recheckDeferOPTION seconds is too low, rounding up to 120 seconds."
recheckDeferSECONDS=120
elif [[ $recheckDeferOPTION -gt 2628288 ]]; then
sendToLog "Warning: Specified recheck deferral time of $recheckDeferOPTION seconds is too high, rounding down to 2628288 seconds (30 days)."
recheckDeferSECONDS=2628288
else
recheckDeferSECONDS=$recheckDeferOPTION
fi
defaults write "$superPLIST" RecheckDefer -string "$recheckDeferSECONDS"
elif [[ -n $recheckDeferOPTION ]] && ! [[ $recheckDeferOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The recheck deferral time must only be a number."; parameterERROR="TRUE"
fi
# Validate $errorDeferOPTION input and if valid override default $errorDeferSECONDS parameter and save to $superPLIST.
if [[ "$errorDeferOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for error deferral."
defaults delete "$superPLIST" ErrorDefer 2> /dev/null
elif [[ -n $errorDeferOPTION ]] && [[ $errorDeferOPTION =~ $regexNUMBER ]]; then
if [[ $errorDeferOPTION -lt 120 ]]; then
sendToLog "Warning: Specified error deferral time of $errorDeferOPTION seconds is too low, rounding up to 120 seconds."
errorDeferSECONDS=120
elif [[ $errorDeferOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified error deferral time of $errorDeferOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
errorDeferSECONDS=86400
else
errorDeferSECONDS=$errorDeferOPTION
fi
defaults write "$superPLIST" ErrorDefer -string "$errorDeferSECONDS"
elif [[ -n $errorDeferOPTION ]] && ! [[ $errorDeferOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The error deferral time must only be a number."; parameterERROR="TRUE"
fi
# Validated that $recheckDeferOPTION and $skipUpdatesOPTION are not both active.
if [[ -n $recheckDeferOPTION ]] && [[ "$skipUpdatesOPTION" == "TRUE" ]]; then
sendToLog "Parameter Error: You can not specify both the --recheck-defer and --skip-updates options at the same time."; parameterERROR="TRUE"
fi
# Validate $focusCountOPTION input and if valid set $focusCountMAX and save to $superPLIST.
if [[ "$focusCountOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for focus count deadline."
defaults delete "$superPLIST" FocusCount 2> /dev/null
elif [[ -n $focusCountOPTION ]] && [[ $focusCountOPTION =~ $regexNUMBER ]]; then
focusCountMAX=$focusCountOPTION
defaults write "$superPLIST" FocusCount -string "$focusCountMAX"
elif [[ -n $focusCountOPTION ]] && ! [[ $focusCountOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The focus count deadline must only be a number."; parameterERROR="TRUE"
fi
# Validate $softCountOPTION input and if valid set $softCountMAX.
if [[ "$softCountOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for soft count deadline."
defaults delete "$superPLIST" SoftCount 2> /dev/null
elif [[ -n $softCountOPTION ]] && [[ $softCountOPTION =~ $regexNUMBER ]]; then
softCountMAX=$softCountOPTION
elif [[ -n $softCountOPTION ]] && ! [[ $softCountOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The soft count deadline must only be a number."; parameterERROR="TRUE"
fi
# Validate $hardCountOPTION input and if valid set $hardCountMAX.
if [[ "$hardCountOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for hard count deadline."
defaults delete "$superPLIST" HardCount 2> /dev/null
elif [[ -n $hardCountOPTION ]] && [[ $hardCountOPTION =~ $regexNUMBER ]]; then
hardCountMAX=$hardCountOPTION
elif [[ -n $hardCountOPTION ]] && ! [[ $hardCountOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The hard count deadline must only be a number."; parameterERROR="TRUE"
fi
# Validated that $softCountMAX and $hardCountMAX are not both active, if not then save $softCountMAX or $hardCountMAX to $superPLIST.
if [[ -n $softCountMAX ]] && [[ -n $hardCountMAX ]]; then
sendToLog "Parameter Error: There cannot be simultaneous deferral maximums for both soft count and hard count deadlines. You must pick one maximum deferral count behavior."; parameterERROR="TRUE"
else
[[ -n $softCountMAX ]] && defaults write "$superPLIST" SoftCount -string "$softCountMAX"
[[ -n $hardCountMAX ]] && defaults write "$superPLIST" HardCount -string "$hardCountMAX"
fi
# Validate $focusDaysOPTION input and if valid set $focusDaysMAX and $focusDaysSECONDS.
if [[ "$focusDaysOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for focus days deadline."
defaults delete "$superPLIST" FocusDays 2> /dev/null
elif [[ -n $focusDaysOPTION ]] && [[ $focusDaysOPTION =~ $regexNUMBER ]]; then
focusDaysMAX=$focusDaysOPTION
focusDaysSECONDS=$((focusDaysMAX*86400))
elif [[ -n $focusDaysOPTION ]] && ! [[ $focusDaysOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The focus days deadline must only be a number."; parameterERROR="TRUE"
fi
# Validate $softDaysOPTION input and if valid set $softDaysMAX and $softDaysSECONDS.
if [[ "$softDaysOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for soft days deadline."
defaults delete "$superPLIST" SoftDays 2> /dev/null
elif [[ -n $softDaysOPTION ]] && [[ $softDaysOPTION =~ $regexNUMBER ]]; then
softDaysMAX=$softDaysOPTION
softDaysSECONDS=$((softDaysMAX*86400))
elif [[ -n $softDaysOPTION ]] && ! [[ $softDaysOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The soft days deadline must only be a number."; parameterERROR="TRUE"
fi
# Validate $hardDaysOPTION input and if valid set $hardDaysMAX and $hardDaysSECONDS.
if [[ "$hardDaysOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for hard days deadline."
defaults delete "$superPLIST" HardDays 2> /dev/null
elif [[ -n $hardDaysOPTION ]] && [[ $hardDaysOPTION =~ $regexNUMBER ]]; then
hardDaysMAX=$hardDaysOPTION
hardDaysSECONDS=$((hardDaysMAX*86400))
elif [[ -n $hardDaysOPTION ]] && ! [[ $hardDaysOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The hard days deadline must only be a number."; parameterERROR="TRUE"
fi
# Validate $focusDaysMAX, $softDaysMAX, and $hardDaysMAX in relation to each other. If valid then save maximum day deadlines to $superPLIST.
if [[ -n $hardDaysMAX ]] && [[ -n $softDaysMAX ]] && [[ $hardDaysMAX -le $softDaysMAX ]]; then
sendToLog "Parameter Error: The maximum hard days deadline of $hardDaysMAX day(s) must be more than the maximum soft days deadline of $softDaysMAX day(s)."; parameterERROR="TRUE"
fi
if [[ -n $hardDaysMAX ]] && [[ -n $focusDaysMAX ]] && [[ $hardDaysMAX -le $focusDaysMAX ]]; then
sendToLog "Parameter Error: The maximum hard days deadline of $hardDaysMAX day(s) must be more than the maximum focus days deadline of $focusDaysMAX day(s)."; parameterERROR="TRUE"
fi
if [[ -n $softDaysMAX ]] && [[ -n $focusDaysMAX ]] && [[ $softDaysMAX -le $focusDaysMAX ]]; then
sendToLog "Parameter Error: The maximum soft days deadline of $softDaysMAX day(s) must be more than the maximum focus days deadline of $focusDaysMAX day(s)."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]]; then
[[ -n $focusDaysMAX ]] && defaults write "$superPLIST" FocusDays -string "$focusDaysMAX"
[[ -n $softDaysMAX ]] && defaults write "$superPLIST" SoftDays -string "$softDaysMAX"
[[ -n $hardDaysMAX ]] && defaults write "$superPLIST" HardDays -string "$hardDaysMAX"
fi
# Validate $zeroDayOPTION, and if valid set $zeroDayOVERRIDE.
if [[ "$zeroDayOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for manual zero day override date."
defaults delete "$superPLIST" ZeroDay 2> /dev/null
elif [[ -n $zeroDayOPTION ]]; then
extractDATE=$(echo "$zeroDayOPTION" | cut -c-10 )
if [[ $extractDATE =~ $regexDATE ]]; then
extractTIME=$(echo "$zeroDayOPTION" | cut -c11- )
if [[ -n $extractTIME ]]; then
extractHOURS=$(echo "$extractTIME" | cut -d: -f2)
[[ -z $extractHOURS ]] && extractHOURS="00"
extractMINUTES=$(echo "$extractTIME" | cut -d: -f3)
[[ -z $extractMINUTES ]] && extractMINUTES="00"
extractTIME="$extractHOURS:$extractMINUTES"
else
extractTIME="00:00"
fi
if [[ $extractTIME =~ $regexTIME ]]; then
calculatedDEADLINE="$extractDATE:$extractTIME"
else
sendToLog "Parameter Error: The manual zero day override date time must be a valid 24-hour time formatted as hh:mm."; parameterERROR="TRUE"
fi
else
sendToLog "Parameter Error: The manual zero day override date must be a valid date formatted as YYYY-MM-DD."; parameterERROR="TRUE"
fi
if [[ $calculatedDEADLINE =~ $regexDATETIME ]]; then
zeroDayOVERRIDE="$calculatedDEADLINE"
else
sendToLog "Parameter Error: The manual zero day override date must be a valid and formatted as YYYY-MM-DD:hh:mm."; parameterERROR="TRUE"
fi
fi
# Validate that any $zeroDayOVERRIDE also includes a day deadline, if valid save to $superPLIST.
if { [[ -z $focusDaysMAX ]] && [[ -z $softDaysMAX ]] && [[ -z $hardDaysMAX ]]; } && [[ -n $zeroDayOVERRIDE ]]; then
sendToLog "Parameter Error: Specifying a manual zero day date also requires that you set a day deadline."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]]; then
[[ -n $zeroDayOVERRIDE ]] && defaults write "$superPLIST" ZeroDay -string "$zeroDayOVERRIDE"
fi
# Validate $focusDateOPTION, if valid set $focusDateMAX and $focusDateEPOCH.
if [[ "$focusDateOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for focus date deadline."
defaults delete "$superPLIST" FocusDate 2> /dev/null
elif [[ -n $focusDateOPTION ]]; then
extractDATE=$(echo "$focusDateOPTION" | cut -c-10 )
if [[ $extractDATE =~ $regexDATE ]]; then
extractTIME=$(echo "$focusDateOPTION" | cut -c11- )
if [[ -n $extractTIME ]]; then
extractHOURS=$(echo "$extractTIME" | cut -d: -f2)
[[ -z $extractHOURS ]] && extractHOURS="00"
extractMINUTES=$(echo "$extractTIME" | cut -d: -f3)
[[ -z $extractMINUTES ]] && extractMINUTES="00"
extractTIME="$extractHOURS:$extractMINUTES"
else
extractTIME="00:00"
fi
if [[ $extractTIME =~ $regexTIME ]]; then
calculatedDEADLINE="$extractDATE:$extractTIME"
else
sendToLog "Parameter Error: The focus date deadline time must be a valid 24-hour time formatted as hh:mm."; parameterERROR="TRUE"
fi
else
sendToLog "Parameter Error: The focus date deadline date must be a valid date formatted as YYYY-MM-DD."; parameterERROR="TRUE"
fi
if [[ $calculatedDEADLINE =~ $regexDATETIME ]]; then
focusDateMAX="$calculatedDEADLINE"
focusDateEPOCH=$(date -j -f "%Y-%m-%d:%H:%M" "$calculatedDEADLINE" +"%s")
else
sendToLog "Parameter Error: The focus date deadline must be a valid and formatted as YYYY-MM-DD:hh:mm."; parameterERROR="TRUE"
fi
fi
# Validate $softDateOPTION, if valid set $softDateMAX and $softDateEPOCH.
if [[ "$softDateOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for soft date deadline."
defaults delete "$superPLIST" SoftDate 2> /dev/null
elif [[ -n $softDateOPTION ]]; then
extractDATE=$(echo "$softDateOPTION" | cut -c-10 )
if [[ $extractDATE =~ $regexDATE ]]; then
extractTIME=$(echo "$softDateOPTION" | cut -c11- )
if [[ -n $extractTIME ]]; then
extractHOURS=$(echo "$extractTIME" | cut -d: -f2)
[[ -z $extractHOURS ]] && extractHOURS="00"
extractMINUTES=$(echo "$extractTIME" | cut -d: -f3)
[[ -z $extractMINUTES ]] && extractMINUTES="00"
extractTIME="$extractHOURS:$extractMINUTES"
else
extractTIME="00:00"
fi
if [[ $extractTIME =~ $regexTIME ]]; then
calculatedDEADLINE="$extractDATE:$extractTIME"
else
sendToLog "Parameter Error: The soft date deadline time must be a valid 24-hour time formatted as hh:mm."; parameterERROR="TRUE"
fi
else
sendToLog "Parameter Error: The soft date deadline date must be a valid date formatted as YYYY-MM-DD."; parameterERROR="TRUE"
fi
if [[ $calculatedDEADLINE =~ $regexDATETIME ]]; then
softDateMAX="$calculatedDEADLINE"
softDateEPOCH=$(date -j -f "%Y-%m-%d:%H:%M" "$calculatedDEADLINE" +"%s")
else
sendToLog "Parameter Error: The soft date deadline must be a valid and formatted as YYYY-MM-DD:hh:mm."; parameterERROR="TRUE"
fi
fi
# Validate $hardDateOPTION, if valid set $hardDateMAX and $hardDateEPOCH.
if [[ "$hardDateOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for hard date deadline."
defaults delete "$superPLIST" HardDate 2> /dev/null
elif [[ -n $hardDateOPTION ]]; then
extractDATE=$(echo "$hardDateOPTION" | cut -c-10 )
if [[ $extractDATE =~ $regexDATE ]]; then
extractTIME=$(echo "$hardDateOPTION" | cut -c11- )
if [[ -n $extractTIME ]]; then
extractHOURS=$(echo "$extractTIME" | cut -d: -f2)
[[ -z $extractHOURS ]] && extractHOURS="00"
extractMINUTES=$(echo "$extractTIME" | cut -d: -f3)
[[ -z $extractMINUTES ]] && extractMINUTES="00"
extractTIME="$extractHOURS:$extractMINUTES"
else
extractTIME="00:00"
fi
if [[ $extractTIME =~ $regexTIME ]]; then
calculatedDEADLINE="$extractDATE:$extractTIME"
else
sendToLog "Parameter Error: The hard date deadline time must be a valid 24-hour time formatted as hh:mm."; parameterERROR="TRUE"
fi
else
sendToLog "Parameter Error: The hard date deadline date must be a valid date formatted as YYYY-MM-DD."; parameterERROR="TRUE"
fi
if [[ $calculatedDEADLINE =~ $regexDATETIME ]]; then
hardDateMAX="$calculatedDEADLINE"
hardDateEPOCH=$(date -j -f "%Y-%m-%d:%H:%M" "$calculatedDEADLINE" +"%s")
else
sendToLog "Parameter Error: The hard date deadline must be a valid and formatted as YYYY-MM-DD:hh:mm."; parameterERROR="TRUE"
fi
fi
# Validate $focusDateEPOCH, $softDateEPOCH, and $hardDateEPOCH in relation to each other. If valid then save date deadlines to $superPLIST.
if [[ -n $hardDateEPOCH ]] && [[ -n $softDateEPOCH ]] && [[ $hardDateEPOCH -le $softDateEPOCH ]]; then
sendToLog "Parameter Error: The hard date deadline of $hardDateMAX must be later than the soft date deadline of $softDateMAX."; parameterERROR="TRUE"
fi
if [[ -n $hardDateEPOCH ]] && [[ -n $focusDateEPOCH ]] && [[ $hardDateEPOCH -le $focusDateEPOCH ]]; then
sendToLog "Parameter Error: The hard date deadline of $hardDateMAX must be later than the focus date deadline of $focusDateMAX."; parameterERROR="TRUE"
fi
if [[ -n $softDateEPOCH ]] && [[ -n $focusDateEPOCH ]] && [[ $softDateEPOCH -le $focusDateEPOCH ]]; then
sendToLog "Parameter Error: The soft date deadline of $softDateMAX must be later than the focus date deadline of $focusDateMAX."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]]; then
[[ -n $focusDateMAX ]] && defaults write "$superPLIST" FocusDate -string "$focusDateMAX"
[[ -n $softDateMAX ]] && defaults write "$superPLIST" SoftDate -string "$softDateMAX"
[[ -n $hardDateMAX ]] && defaults write "$superPLIST" HardDate -string "$hardDateMAX"
fi
# Validate that any focus deadlines also include a coordinating $focusDeferSECONDS, if not then set $focusDeferSECONDS to $defaultDeferSECONDS.
if { [[ -n $focusCountMAX ]] || [[ -n $focusDaysMAX ]] || [[ -n $focusDateMAX ]]; } && [[ -z $focusDeferSECONDS ]]; then
sendToLog "Warning: No focus defer seconds specified, setting to default defer of $defaultDeferSECONDS seconds."
focusDeferSECONDS="$defaultDeferSECONDS"
fi
# Validate $deferDialogTimeoutOPTION and if valid set $deferDialogTimeoutSECONDS.
if [[ "$deferDialogTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for restart/defer dialog timeout."
defaults delete "$superPLIST" DeferDialogTimeout 2> /dev/null
elif [[ -n $deferDialogTimeoutOPTION ]] && [[ $deferDialogTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ $deferDialogTimeoutOPTION -lt 60 ]]; then
sendToLog "Warning: Specified restart/defer dialog timeout of $deferDialogTimeoutOPTION seconds is too low, rounding up to 60 seconds."
deferDialogTimeoutSECONDS=60
elif [[ $deferDialogTimeoutOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified restart/defer dialog timeout of $deferDialogTimeoutOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
deferDialogTimeoutSECONDS=86400
else
deferDialogTimeoutSECONDS=$deferDialogTimeoutOPTION
fi
defaults write "$superPLIST" DeferDialogTimeout -string "$deferDialogTimeoutSECONDS"
elif [[ -n $deferDialogTimeoutOPTION ]] && ! [[ $deferDialogTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The restart/defer dialog timeout must only be a number."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $deferDialogTimeoutSECONDS ]] && [[ -n $displayRedrawSECONDS ]]; then
displayMinimumTIMEOUT=$((displayRedrawSECONDS * 3))
if [[ $deferDialogTimeoutSECONDS -lt $displayMinimumTIMEOUT ]];then
sendToLog "Warning: Specified restart/defer dialog timeout of $deferDialogTimeoutSECONDS seconds is too low given a display redraw of $displayRedrawSECONDS seconds, changing restart/defer dialog timeout to $displayMinimumTIMEOUT seconds."
deferDialogTimeoutSECONDS=$displayMinimumTIMEOUT
defaults write "$superPLIST" DeferDialogTimeout -string "$deferDialogTimeoutSECONDS"
fi
fi
# Validate $softDialogTimeoutOPTION and if valid set $softDialogTimeoutSECONDS.
if [[ "$softDialogTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for soft deadline dialog timeout."
defaults delete "$superPLIST" SoftDialogTimeout 2> /dev/null
elif [[ -n $softDialogTimeoutOPTION ]] && [[ $softDialogTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ $softDialogTimeoutOPTION -lt 60 ]]; then
sendToLog "Warning: Specified soft deadline dialog timeout of $softDialogTimeoutOPTION seconds is too low, rounding up to 60 seconds."
softDialogTimeoutSECONDS=60
elif [[ $softDialogTimeoutOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified soft deadline dialog timeout of $softDialogTimeoutOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
softDialogTimeoutSECONDS=86400
else
softDialogTimeoutSECONDS=$softDialogTimeoutOPTION
fi
defaults write "$superPLIST" SoftDialogTimeout -string "$softDialogTimeoutSECONDS"
elif [[ -n $softDialogTimeoutOPTION ]] && ! [[ $softDialogTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The soft deadline dialog timeout must only be a number."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $softDialogTimeoutSECONDS ]] && [[ -n $displayRedrawSECONDS ]]; then
displayMinimumTIMEOUT=$((displayRedrawSECONDS * 3))
if [[ $softDialogTimeoutSECONDS -lt $displayMinimumTIMEOUT ]];then
sendToLog "Warning: Specified soft deadline dialog timeout of $softDialogTimeoutSECONDS seconds is too low given a display redraw of $displayRedrawSECONDS seconds, changing soft deadline dialog timeout to $displayMinimumTIMEOUT seconds."
softDialogTimeoutSECONDS=$displayMinimumTIMEOUT
defaults write "$superPLIST" SoftDialogTimeout -string "$softDialogTimeoutSECONDS"
fi
fi
# Validate $displayRedrawOPTION input and if valid set $displayRedrawSECONDS.
if [[ "$displayRedrawOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display redraw."
defaults delete "$superPLIST" DisplayRedraw 2> /dev/null
elif [[ -n $displayRedrawOPTION ]] && [[ $displayRedrawOPTION =~ $regexNUMBER ]]; then
if [[ $displayRedrawOPTION -lt 10 ]]; then
sendToLog "Warning: Specified display redraw of $displayRedrawOPTION seconds is too low, rounding up to 10 seconds."
displayRedrawSECONDS=10
elif [[ $displayRedrawOPTION -gt 3600 ]]; then
sendToLog "Warning: Specified display redraw of $displayRedrawOPTION seconds is too high, rounding down to 3600 seconds (1 hour)."
displayRedrawSECONDS=3600
else
displayRedrawSECONDS=$displayRedrawOPTION
fi
defaults write "$superPLIST" DisplayRedraw -string "$displayRedrawSECONDS"
elif [[ -n $displayRedrawOPTION ]] && ! [[ $displayRedrawOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The display redraw time must only be a number."; parameterERROR="TRUE"
fi
# Verify the $displayIconOPTION to be used for the super service account and in notifications and dialogs, and if valid copy and set $cachedICON and save to $superPLIST.
if [[ "$displayIconOPTION" == "X" ]]; then
sendToLog "Startup: Deleting cached display icon."
[[ -f "$cachedICON" ]] && rm -f "$cachedICON"
elif [[ -n "$displayIconOPTION" ]] && [[ "$displayIconOPTION" != "$(defaults read "$superPLIST" DisplayIconCache 2> /dev/null)" ]]; then
if [[ $(echo "$displayIconOPTION" | grep -c '^http://\|^https://') -gt 0 ]]; then
sendToLog "Startup: Attempting to download requested icon from: $displayIconOPTION"
downloadRESULT=$(curl "$displayIconOPTION" -L -o "/tmp/cachedICON" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: downloadRESULT is:\n$downloadRESULT"
if [[ -f "/tmp/cachedICON" ]]; then
sipsRESULT=$(sips -s format png "/tmp/cachedICON" --out "$cachedICON" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: sipsRESULT is:\n$sipsRESULT"
defaults write "$superPLIST" DisplayIconCache -string "$displayIconOPTION"
else
sendToLog "Warning: Unable to download specified icon from: $displayIconOPTION"
fi
elif [[ -e "$displayIconOPTION" ]]; then
sendToLog "Startup: Copying requested icon from: $displayIconOPTION"
sipsRESULT=$(sips -s format png "$displayIconOPTION" --out "$cachedICON" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: sipsRESULT is:\n$sipsRESULT"
defaults write "$superPLIST" DisplayIconCache -string "$displayIconOPTION"
else
sendToLog "Warning: Unable to locate specified icon from: $displayIconOPTION"
fi
fi
if [[ ! -f "$cachedICON" ]]; then
sendToLog "Startup: No custom display icon found, copying default icon from: $defaultICON"
sipsRESULT=$(sips -s format png "$defaultICON" --out "$cachedICON" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: sipsRESULT is:\n$sipsRESULT"
defaults write "$superPLIST" DisplayIconCache -string "$defaultICON"
fi
# Validate $iconSizeIbmOPTION input and if valid override default $ibmNotifierIconSIZE parameter and save to $superPLIST.
if [[ "$iconSizeIbmOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for IBM Notifier icon size."
defaults delete "$superPLIST" IconSizeIbm 2> /dev/null
elif [[ -n $iconSizeIbmOPTION ]] && [[ $iconSizeIbmOPTION =~ $regexNUMBER ]]; then
if [[ $iconSizeIbmOPTION -lt 32 ]]; then
sendToLog "Warning: Specified IBM Notifier icon size of $iconSizeIbmOPTION pixels is too low, rounding up to 32 pixels."
ibmNotifierIconSIZE=32
elif [[ $iconSizeIbmOPTION -gt 150 ]]; then
sendToLog "Warning: Specified IBM Notifier icon size of $iconSizeIbmOPTION pixels is too high, rounding down to 150 pixels."
ibmNotifierIconSIZE=150
else
ibmNotifierIconSIZE=$iconSizeIbmOPTION
fi
defaults write "$superPLIST" IconSizeIbm -string "$ibmNotifierIconSIZE"
elif [[ -n $iconSizeIbmOPTION ]] && ! [[ $iconSizeIbmOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The IBM Notifier icon size must only be a number."; parameterERROR="TRUE"
fi
# Validate $iconSizeJamfOPTION input and if valid override default $jamfHelperIconSIZE parameter and save to $superPLIST.
if [[ "$iconSizeJamfOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for IBM Notifier icon size."
defaults delete "$superPLIST" IconSizeJamf 2> /dev/null
elif [[ -n $iconSizeJamfOPTION ]] && [[ $iconSizeJamfOPTION =~ $regexNUMBER ]]; then
if [[ $iconSizeJamfOPTION -lt 32 ]]; then
sendToLog "Warning: Specified IBM Notifier icon size of $iconSizeJamfOPTION pixels is too low, rounding up to 32 pixels."
jamfHelperIconSIZE=32
elif [[ $iconSizeJamfOPTION -gt 150 ]]; then
sendToLog "Warning: Specified IBM Notifier icon size of $iconSizeJamfOPTION pixels is too high, rounding down to 150 pixels."
jamfHelperIconSIZE=150
else
jamfHelperIconSIZE=$iconSizeJamfOPTION
fi
defaults write "$superPLIST" IconSizeJamf -string "$jamfHelperIconSIZE"
elif [[ -n $iconSizeJamfOPTION ]] && ! [[ $iconSizeJamfOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The IBM Notifier icon size must only be a number."; parameterERROR="TRUE"
fi
# Validate $displayAccessoryTypeOPTION, $displayAccessoryDefaultOPTION, $displayAccessoryUpdateOPTION, and $displayAccessoryUpgradeOPTION.
if [[ "$displayAccessoryTypeOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display accessory."
defaults delete "$superPLIST" DisplayAccessoryType 2> /dev/null
unset displayAccessoryTypeOPTION
fi
if [[ "$displayAccessoryDefaultOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display accessory default content."
defaults delete "$superPLIST" DisplayAccessoryDefault 2> /dev/null
unset displayAccessoryDefaultOPTION
fi
if [[ "$displayAccessoryUpdateOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display accessory update content."
defaults delete "$superPLIST" DisplayAccessoryUpdate 2> /dev/null
unset displayAccessoryUpdateOPTION
fi
if [[ "$displayAccessoryUpgradeOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display accessory upgrade content."
defaults delete "$superPLIST" DisplayAccessoryUpgrade 2> /dev/null
unset displayAccessoryUpgradeOPTION
fi
if [[ "$displayAccessoryUserAuthOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for display accessory user authentication content."
defaults delete "$superPLIST" DisplayAccessoryUserAuth 2> /dev/null
unset displayAccessoryUserAuthOPTION
fi
if [[ -n $displayAccessoryTypeOPTION ]] && [[ -z $displayAccessoryDefaultOPTION ]] && [[ -z $displayAccessoryUpdateOPTION ]] && [[ -z $displayAccessoryUpgradeOPTION ]] && [[ -z $displayAccessoryUserAuthOPTION ]]; then
sendToLog "Parameter Error: To use a display accessory type you must also specify one of the display accessory content options."; parameterERROR="TRUE"
fi
if [[ -z $displayAccessoryTypeOPTION ]] && { [[ -n $displayAccessoryDefaultOPTION ]] || [[ -n $displayAccessoryUpdateOPTION ]] || [[ -n $displayAccessoryUpgradeOPTION ]] || [[ -n $displayAccessoryUserAuthOPTION ]]; }; then
sendToLog "Parameter Error: To use any of the display accessory content options you must also specify the display accessory type."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $displayAccessoryTypeOPTION ]]; then
if [[ $displayAccessoryTypeOPTION =~ ^TEXTBOX$|^HTMLBOX$|^HTML$|^IMAGE$|^VIDEO$|^VIDEOAUTO$ ]]; then
displayAccessoryTYPE="$displayAccessoryTypeOPTION"
defaults write "$superPLIST" DisplayAccessoryType -string "$displayAccessoryTypeOPTION"
else
sendToLog "Parameter Error: Unrecognized dialog display accessory type: $displayAccessoryTypeOPTION. You must specify one of the following; TEXTBOX, HTMLBOX, HTML, IMAGE, VIDEO, or VIDEOAUTO."; parameterERROR="TRUE"
fi
fi
if [[ "$parameterERROR" != "TRUE" ]] && { [[ -n $displayAccessoryDefaultOPTION ]] || [[ -n $displayAccessoryUpdateOPTION ]] || [[ -n $displayAccessoryUpgradeOPTION ]] || [[ -n $displayAccessoryUserAuthOPTION ]]; }; then
if [[ -n $displayAccessoryDefaultOPTION ]]; then
displayAccessoryDefaultCONTENT="$displayAccessoryDefaultOPTION"
defaults write "$superPLIST" DisplayAccessoryDefault -string "$displayAccessoryDefaultOPTION"
fi
if [[ -n $displayAccessoryUpdateOPTION ]]; then
displayAccessoryUpdateCONTENT="$displayAccessoryUpdateOPTION"
defaults write "$superPLIST" DisplayAccessoryUpdate -string "$displayAccessoryUpdateOPTION"
fi
if [[ -n $displayAccessoryUpgradeOPTION ]]; then
displayAccessoryUpgradeCONTENT="$displayAccessoryUpgradeOPTION"
defaults write "$superPLIST" DisplayAccessoryUpgrade -string "$displayAccessoryUpgradeOPTION"
fi
if [[ -n $displayAccessoryUserAuthOPTION ]]; then
displayAccessoryUserAuthCONTENT="$displayAccessoryUserAuthOPTION"
defaults write "$superPLIST" DisplayAccessoryUserAuth -string "$displayAccessoryUserAuthOPTION"
fi
fi
# Validate $helpButtonOPTION and set $helpBUTTON and save to $superPLIST.
if [[ "$helpButtonOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for help button option."
defaults delete "$superPLIST" HelpButton 2> /dev/null
elif [[ -n $helpButtonOPTION ]]; then
helpBUTTON="$helpButtonOPTION"
defaults write "$superPLIST" HelpButton -string "$helpButtonOPTION"
fi
# Validate $warningButtonOPTION and set $warningBUTTON and save to $superPLIST.
if [[ "$warningButtonOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for help button option."
defaults delete "$superPLIST" WarningButton 2> /dev/null
elif [[ -n $warningButtonOPTION ]]; then
warningBUTTON="$warningButtonOPTION"
defaults write "$superPLIST" WarningButton -string "$warningButtonOPTION"
fi
# Manage $displaySilentlyOPTION and save to $superPLIST.
if [[ $displaySilentlyOPTION -eq 1 ]] || [[ "$displaySilentlyOPTION" == "TRUE" ]]; then
displaySilentlyOPTION="TRUE"
defaults write "$superPLIST" DisplaySilently -bool true
else
displaySilentlyOPTION="FALSE"
defaults delete "$superPLIST" DisplaySilently 2> /dev/null
fi
# Validate $userAuthTimeoutOPTION input and if valid set $userAuthTimeoutSECONDS and save to $superPLIST.
if [[ "$userAuthTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for user authentication dialog timeout seconds."
defaults delete "$superPLIST" UserAuthTimeout 2> /dev/null
elif [[ -n $userAuthTimeoutOPTION ]] && [[ $userAuthTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ $userAuthTimeoutOPTION -lt 60 ]]; then
sendToLog "Warning: Specified user authentication dialog timeout of $userAuthTimeoutOPTION seconds is too low, rounding up to 60 seconds."
userAuthTimeoutSECONDS=60
elif [[ $userAuthTimeoutOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified user authentication dialog timeout of $userAuthTimeoutOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
userAuthTimeoutSECONDS=86400
else
userAuthTimeoutSECONDS=$userAuthTimeoutOPTION
fi
defaults write "$superPLIST" UserAuthTimeout -string "$userAuthTimeoutSECONDS"
elif [[ -n $userAuthTimeoutOPTION ]] && ! [[ $userAuthTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The user authentication dialog timeout seconds must only be a number."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $displayRedrawSECONDS ]]; then
displayMinimumTIMEOUT=$((displayRedrawSECONDS * 3))
if [[ $userAuthTimeoutSECONDS -lt $displayMinimumTIMEOUT ]];then
sendToLog "Warning: Specified user authentication dialog timeout of $userAuthTimeoutSECONDS seconds is too low given a display redraw of $displayRedrawSECONDS seconds, changing user authentication dialog timeout to $displayMinimumTIMEOUT seconds."
userAuthTimeoutSECONDS=$displayMinimumTIMEOUT
defaults write "$superPLIST" UserAuthTimeout -string "$userAuthTimeoutSECONDS"
fi
fi
# Validate $userAuthMDMFailoverOPTION and $displayAccessoryContentOPTION.
userAuthMDMFAILOVER="FALSE"
if [[ "$userAuthMDMFailoverOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for user authentication MDM failover."
defaults delete "$superPLIST" UserAuthMDMFailover 2> /dev/null
elif [[ -n $userAuthMDMFailoverOPTION ]]; then
oldIFS="$IFS"; IFS=','
read -r -a userAuthMDMFailoverARRAY <<< "$userAuthMDMFailoverOPTION"
for option in "${userAuthMDMFailoverARRAY[@]}"; do
if [[ $option =~ ^ALWAYS$ ]]; then
userAuthMDMFAILOVER="TRUE"
elif [[ $option =~ ^NOSERVICE$ ]]; then
userAuthMDMFailoverNOSERVICE="TRUE"
elif [[ $option =~ ^SOFT$ ]]; then
userAuthMDMFailoverSOFT="TRUE"
elif [[ $option =~ ^HARD$ ]]; then
userAuthMDMFailoverHARD="TRUE"
elif [[ $option =~ ^INSTALLNOW$ ]]; then
userAuthMDMFailoverINSTALLNOW="TRUE"
elif [[ $option =~ ^BOOTSTRAP$ ]]; then
userAuthMDMFailoverBOOTSTRAP="TRUE"
else
sendToLog "Parameter Error: Unrecognized user authentication MDM failover type: $option. You can specify any of the following types separated by commas (no spaces); ALWAYS,SOFT,HARD,INSTALLNOW,BOOTSTRAP."; parameterERROR="TRUE"
fi
done
IFS="$oldIFS"
[[ "$parameterERROR" != "TRUE" ]] && defaults write "$superPLIST" UserAuthMDMFailover -string "$userAuthMDMFailoverOPTION"
fi
# Manage $allowUpgradeOPTION and save to $superPLIST.
if [[ $allowUpgradeOPTION -eq 1 ]] || [[ "$allowUpgradeOPTION" == "TRUE" ]]; then
allowUpgradeOPTION="TRUE"
defaults write "$superPLIST" AllowUpgrade -bool true
else
allowUpgradeOPTION="FALSE"
defaults delete "$superPLIST" AllowUpgrade 2> /dev/null
fi
# Manage $targetUpgradeOPTION and if a valid macOS version then save to $superPLIST.
if [[ "$targetUpgradeOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for macOS target upgrade version."
defaults delete "$superPLIST" TargetUpgrade 2> /dev/null
unset targetUpgradeOPTION
elif [[ -n $targetUpgradeOPTION ]] && ! [[ $targetUpgradeOPTION =~ $regexMACOSMAJORVERSION ]]; then
sendToLog "Parameter Error: The upgrade target version must be a contemporary macOS version number (11,12,etc.)."; parameterERROR="TRUE"
elif [[ -n $targetUpgradeOPTION ]] && [[ $targetUpgradeOPTION =~ $regexMACOSMAJORVERSION ]]; then
if [[ "$allowUpgradeOPTION" == "TRUE" ]]; then
targetUpgradeVERSION="$targetUpgradeOPTION"
defaults write "$superPLIST" TargetUpgrade -string "$targetUpgradeOPTION"
else
sendToLog "Parameter Error: To specify a target upgrade version you must also use --allow-upgrade."; parameterERROR="TRUE"
defaults delete "$superPLIST" TargetUpgrade 2> /dev/null
fi
fi
# Manage $onlyDownloadOPTION and save to $superPLIST.
if [[ $onlyDownloadOPTION -eq 1 ]] || [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
onlyDownloadOPTION="TRUE"
defaults write "$superPLIST" OnlyDownload -bool true
else
onlyDownloadOPTION="FALSE"
defaults delete "$superPLIST" OnlyDownload 2> /dev/null
fi
# Manage $installNowOPTION and save to $superPLIST.
if [[ $installNowOPTION -eq 1 ]] || [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$currentUserNAME" != "FALSE" ]]; then
installNowOPTION="TRUE"
defaults write "$superPLIST" InstallNow -bool true
else
sendToLog "Parameter Error: The --install-now option can only be used when a user is actively logged in."; parameterERROR="TRUE"
installNowOPTION="FALSE"
defaults delete "$superPLIST" InstallNow 2> /dev/null
fi
else
installNowOPTION="FALSE"
defaults delete "$superPLIST" InstallNow 2> /dev/null
fi
if [[ "$onlyDownloadOPTION" == "TRUE" ]] && [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Warning: When both the --only-download and --install-now options are enabled the --install-now option takes priority."
onlyDownloadOPTION="FALSE"
fi
# Manage $allowRSRUpdatesOPTION and save to $superPLIST.
if [[ $allowRSRUpdatesOPTION -eq 1 ]] || [[ "$allowRSRUpdatesOPTION" == "TRUE" ]]; then
allowRSRUpdatesOPTION="TRUE"
defaults write "$superPLIST" AllowRSRUpdates -bool true
else
allowRSRUpdatesOPTION="FALSE"
defaults delete "$superPLIST" AllowRSRUpdates 2> /dev/null
fi
# Manage $enforceNonSystemUpdatesOPTION and save to $superPLIST.
if [[ $enforceNonSystemUpdatesOPTION -eq 1 ]] || [[ "$enforceNonSystemUpdatesOPTION" == "TRUE" ]]; then
enforceNonSystemUpdatesOPTION="TRUE"
defaults write "$superPLIST" EnforceNonSystemUpdates -bool true
else
enforceNonSystemUpdatesOPTION="FALSE"
defaults delete "$superPLIST" EnforceNonSystemUpdates 2> /dev/null
fi
# Validate $policyTriggersOPTION input and if valid set $policyTRIGGERS and save to $superPLIST.
if [[ "$policyTriggersOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for Jamf Pro Policy triggers."
defaults delete "$superPLIST" PolicyTriggers 2> /dev/null
elif [[ -n $policyTriggersOPTION ]]; then
policyTRIGGERS="$policyTriggersOPTION"
defaults write "$superPLIST" PolicyTriggers -string "$policyTRIGGERS"
fi
if [[ "$jamfVERSION" == "FALSE" ]] && [[ -n $policyTRIGGERS ]]; then
sendToLog "Parameter Error: Unable to run Jamf Pro Policy Triggers due to missing Jamf binary."; parameterERROR="TRUE"
fi
# Manage $skipUpdatesOPTION and save to $superPLIST.
if [[ $skipUpdatesOPTION -eq 1 ]] || [[ "$skipUpdatesOPTION" == "TRUE" ]]; then
skipUpdatesOPTION="TRUE"
defaults write "$superPLIST" SkipUpdates -bool true
else
skipUpdatesOPTION="FALSE"
defaults delete "$superPLIST" SkipUpdates 2> /dev/null
fi
if [[ "$skipUpdatesOPTION" == "TRUE" ]] && [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "Parameter Error: The --restart-without-updates option can not be used with the --only-download option."; parameterERROR="TRUE"
fi
# Manage $restartWithoutUpdatesOPTION and save to $superPLIST.
if [[ $restartWithoutUpdatesOPTION -eq 1 ]] || [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]]; then
restartWithoutUpdatesOPTION="TRUE"
defaults write "$superPLIST" RestartWithoutUpdates -bool true
else
restartWithoutUpdatesOPTION="FALSE"
defaults delete "$superPLIST" RestartWithoutUpdates 2> /dev/null
fi
if [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]] && [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "Parameter Error: The --restart-without-updates option can not be used with the --only-download option."; parameterERROR="TRUE"
fi
# Manage $testModeOPTION and save to $superPLIST.
if [[ $testModeOPTION -eq 1 ]] || [[ "$testModeOPTION" == "TRUE" ]]; then
testModeOPTION="TRUE"
defaults write "$superPLIST" TestMode -bool true
else
testModeOPTION="FALSE"
defaults delete "$superPLIST" TestMode 2> /dev/null
fi
if [[ "$testModeOPTION" == "TRUE" ]] && [[ "$currentUserNAME" == "FALSE" ]]; then
sendToLog "Parameter Error: Test mode requires that a valid user is logged in."; parameterERROR="TRUE"
fi
if [[ "$testModeOPTION" == "TRUE" ]] && [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "Parameter Error: The --test-mode option can not be used with the --only-download option."; parameterERROR="TRUE"
fi
# Validate $freeSpaceUpdateOPTION input and if valid set $freeSpaceUpdateGB and save to $superPLIST.
if [[ "$freeSpaceUpdateOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for free space update gigabytes."
defaults delete "$superPLIST" FreeSpaceUpdate 2> /dev/null
elif [[ -n $freeSpaceUpdateOPTION ]] && [[ $freeSpaceUpdateOPTION =~ $regexNUMBER ]]; then
[[ $freeSpaceUpdateOPTION -ne $freeSpaceUpdateGB ]] && sendToLog "Warning: Specifying a custom free space update gigabytes should only be used for testing purposes."
freeSpaceUpdateGB=$freeSpaceUpdateOPTION
defaults write "$superPLIST" FreeSpaceUpdate -string "$freeSpaceUpdateGB"
elif [[ -n $freeSpaceUpdateOPTION ]] && ! [[ $freeSpaceUpdateOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The free space update gigabytes must only be a number."; parameterERROR="TRUE"
fi
# Validate $freeSpaceUpgradeOPTION input and if valid set $freeSpaceUpgradeGB and save to $superPLIST.
if [[ "$freeSpaceUpgradeOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for free space upgrade gigabytes."
defaults delete "$superPLIST" FreeSpaceUpgrade 2> /dev/null
elif [[ -n $freeSpaceUpgradeOPTION ]] && [[ $freeSpaceUpgradeOPTION =~ $regexNUMBER ]]; then
[[ $freeSpaceUpgradeOPTION -ne $freeSpaceUpgradeGB ]] && sendToLog "Warning: Specifying a custom free space upgrade gigabytes should only be used for testing purposes."
freeSpaceUpgradeGB=$freeSpaceUpgradeOPTION
defaults write "$superPLIST" FreeSpaceUpgrade -string "$freeSpaceUpgradeGB"
elif [[ -n $freeSpaceUpgradeOPTION ]] && ! [[ $freeSpaceUpgradeOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The free space upgrade gigabytes must only be a number."; parameterERROR="TRUE"
fi
# Validate $freeSpaceTimeoutOPTION input and if valid set $freeSpaceTimeoutSECONDS and save to $superPLIST.
if [[ "$freeSpaceTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for free space notification timeout seconds."
defaults delete "$superPLIST" FreeSpaceTimeout 2> /dev/null
elif [[ -n $freeSpaceTimeoutOPTION ]] && [[ $freeSpaceTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ $freeSpaceTimeoutOPTION -lt 60 ]]; then
sendToLog "Warning: Specified free space notification timeout of $freeSpaceTimeoutOPTION seconds is too low, rounding up to 60 seconds."
freeSpaceTimeoutSECONDS=60
elif [[ $freeSpaceTimeoutOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified free space notification timeout of $freeSpaceTimeoutOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
freeSpaceTimeoutSECONDS=86400
else
freeSpaceTimeoutSECONDS=$freeSpaceTimeoutOPTION
fi
defaults write "$superPLIST" FreeSpaceTimeout -string "$freeSpaceTimeoutSECONDS"
elif [[ -n $freeSpaceTimeoutOPTION ]] && ! [[ $freeSpaceTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The free space notification timeout seconds must only be a number."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $displayRedrawSECONDS ]]; then
displayMinimumTIMEOUT=$((displayRedrawSECONDS * 3))
if [[ $freeSpaceTimeoutSECONDS -lt $displayMinimumTIMEOUT ]];then
sendToLog "Warning: Specified free space notification timeout of $freeSpaceTimeoutSECONDS seconds is too low given a display redraw of $displayRedrawSECONDS seconds, changing free space notification timeout to $displayMinimumTIMEOUT seconds."
freeSpaceTimeoutSECONDS=$displayMinimumTIMEOUT
defaults write "$superPLIST" FreeSpaceTimeout -string "$freeSpaceTimeoutSECONDS"
fi
fi
# Validate $batteryLevelOPTION input and if valid set $batteryLevelPERCENT and save to $superPLIST.
if [[ "$batteryLevelOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for battery level percentage."
defaults delete "$superPLIST" BatteryLevel 2> /dev/null
elif [[ -n $batteryLevelOPTION ]] && [[ $batteryLevelOPTION =~ $regexNUMBER ]]; then
[[ $batteryLevelOPTION -ne $batteryLevelPERCENT ]] && sendToLog "Warning: Specifying a custom battery level percentage should only be used for testing purposes."
batteryLevelPERCENT=$batteryLevelOPTION
defaults write "$superPLIST" BatteryLevel -string "$batteryLevelPERCENT"
elif [[ -n $batteryLevelOPTION ]] && ! [[ $batteryLevelOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The battery level percentage must only be a number."; parameterERROR="TRUE"
fi
# Validate $batteryTimeoutOPTION input and if valid set $batteryTimeoutSECONDS and save to $superPLIST.
if [[ "$batteryTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for battery level notification timeout seconds."
defaults delete "$superPLIST" BatteryTimeout 2> /dev/null
elif [[ -n $batteryTimeoutOPTION ]] && [[ $batteryTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ $batteryTimeoutOPTION -lt 120 ]]; then
sendToLog "Warning: Specified battery level notification timeout of $batteryTimeoutOPTION seconds is too low, rounding up to 60 seconds."
batteryTimeoutSECONDS=60
elif [[ $batteryTimeoutOPTION -gt 86400 ]]; then
sendToLog "Warning: Specified battery level notification timeout of $batteryTimeoutOPTION seconds is too high, rounding down to 86400 seconds (1 day)."
batteryTimeoutSECONDS=86400
else
batteryTimeoutSECONDS=$batteryTimeoutOPTION
fi
defaults write "$superPLIST" BatteryTimeout -string "$batteryTimeoutSECONDS"
elif [[ -n $batteryTimeoutOPTION ]] && ! [[ $batteryTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The battery level notification timeout seconds must only be a number."; parameterERROR="TRUE"
fi
if [[ "$parameterERROR" != "TRUE" ]] && [[ -n $displayRedrawSECONDS ]]; then
displayMinimumTIMEOUT=$((displayRedrawSECONDS * 3))
if [[ $batteryTimeoutSECONDS -lt $displayMinimumTIMEOUT ]];then
sendToLog "Warning: Specified battery level notification timeout of $batteryTimeoutSECONDS seconds is too low given a display redraw of $displayRedrawSECONDS seconds, changing battery level notification timeout to $displayMinimumTIMEOUT seconds."
batteryTimeoutSECONDS=$displayMinimumTIMEOUT
defaults write "$superPLIST" BatteryTimeout -string "$batteryTimeoutSECONDS"
fi
fi
# Validate $testModeTimeoutOPTION input and if valid set $testModeTimeoutSECONDS and save to $superPLIST.
if [[ "$testModeTimeoutOPTION" == "X" ]]; then
sendToLog "Startup: Deleting local preference for test mode timeout."
defaults delete "$superPLIST" TestModeTimeout 2> /dev/null
elif [[ -n $testModeTimeoutOPTION ]] && [[ $testModeTimeoutOPTION =~ $regexNUMBER ]]; then
if [[ -n $displayRedrawSECONDS ]]; then
if [[ $testModeTimeoutOPTION -lt 30 ]]; then
sendToLog "Warning: Specified test mode timeout of $testModeTimeoutOPTION seconds is too low given that display redraw is also enabled, rounding up to 30 seconds."
testModeTimeoutSECONDS=30
elif [[ $testModeTimeoutOPTION -gt 120 ]]; then
sendToLog "Warning: Specified test mode timeout of $testModeTimeoutOPTION seconds is too high, rounding down to 120 seconds (2 minutes)."
testModeTimeoutSECONDS=120
else
testModeTimeoutSECONDS=$testModeTimeoutOPTION
fi
redrawMaximumTIMEOUT=$((testModeTimeoutSECONDS / 3))
if [[ "$testModeOPTION" == "TRUE" ]] && [[ $displayRedrawSECONDS -gt $redrawMaximumTIMEOUT ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the display redraw option from $displayRedrawSECONDS seconds to $redrawMaximumTIMEOUT seconds. This adjustment is not saved."
displayRedrawSECONDS=$redrawMaximumTIMEOUT
fi
else # No display redraw enabled.
if [[ $testModeTimeoutOPTION -lt 10 ]]; then
sendToLog "Warning: Specified test mode timeout of $testModeTimeoutOPTION seconds is too low, rounding up to 10 seconds."
testModeTimeoutSECONDS=10
elif [[ $testModeTimeoutOPTION -gt 120 ]]; then
sendToLog "Warning: Specified test mode timeout of $testModeTimeoutOPTION seconds is too high, rounding down to 120 seconds (2 minutes)."
testModeTimeoutSECONDS=120
else
testModeTimeoutSECONDS=$testModeTimeoutOPTION
fi
fi
defaults write "$superPLIST" TestModeTimeout -string "$testModeTimeoutSECONDS"
if [[ "$testModeOPTION" == "TRUE" ]]; then
if [[ -n $deferDialogTimeoutSECONDS ]] && [[ $deferDialogTimeoutSECONDS -gt $testModeTimeoutSECONDS ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the restart/defer dialog timeout from $deferDialogTimeoutSECONDS seconds to $testModeTimeoutSECONDS seconds. This adjustment is not saved."
deferDialogTimeoutSECONDS=$testModeTimeoutSECONDS
fi
if [[ -n $softDialogTimeoutSECONDS ]] && [[ $softDialogTimeoutSECONDS -gt $testModeTimeoutSECONDS ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the soft deadline dialog timeout from $softDialogTimeoutSECONDS seconds to $testModeTimeoutSECONDS seconds. This adjustment is not saved."
softDialogTimeoutSECONDS=$testModeTimeoutSECONDS
fi
if [[ $userAuthTimeoutSECONDS -gt $testModeTimeoutSECONDS ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the user authentication dialog timeout from $userAuthTimeoutSECONDS seconds to $testModeTimeoutSECONDS seconds. This adjustment is not saved."
userAuthTimeoutSECONDS=$testModeTimeoutSECONDS
fi
if [[ $freeSpaceTimeoutSECONDS -gt $testModeTimeoutSECONDS ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the free space notification timeout from $freeSpaceTimeoutSECONDS seconds to $testModeTimeoutSECONDS seconds. This adjustment is not saved."
freeSpaceTimeoutSECONDS=$testModeTimeoutSECONDS
fi
if [[ $batteryTimeoutSECONDS -gt $testModeTimeoutSECONDS ]]; then
sendToLog "Warning: Test mode requires temporary adjustment of the battery level notification timeout from $batteryTimeoutSECONDS seconds to $testModeTimeoutSECONDS seconds. This adjustment is not saved."
batteryTimeoutSECONDS=$testModeTimeoutSECONDS
fi
fi
elif [[ -n $testModeTimeoutOPTION ]] && ! [[ $testModeTimeoutOPTION =~ $regexNUMBER ]]; then
sendToLog "Parameter Error: The test mode timeout must only be a number."; parameterERROR="TRUE"
fi
}
# For Apple Silicon computers this function manages update/upgrade credentials given $deleteACCOUNTS, $localACCOUNT, $adminACCOUNT, $superACCOUNT, or $jamfACCOUNT. Any errors set $credentialERROR.
manageUpdateCredentials () {
credentialERROR="FALSE"
# Validate that $localOPTION and $localPASSWORD are simultaneously provided.
if [[ -n $localOPTION ]] && [[ -z $localPASSWORD ]]; then
sendToLog "Credential Error: A local volume owner account name requires that you also set a local volume owner password."; credentialERROR="TRUE"
fi
if [[ -z $localOPTION ]] && [[ -n $localPASSWORD ]]; then
sendToLog "Credential Error: A local volume owner password requires that you also set a local volume owner account name."; credentialERROR="TRUE"
fi
# Validate that $localOPTION exists, is a volume owner, and that $localPASSWORD is correct.
if [[ -n $localOPTION ]] && [[ "$credentialERROR" != "TRUE" ]]; then
localGUID=$(dscl . read "/Users/$localOPTION" GeneratedUID 2> /dev/null | awk '{print $2;}')
if [[ -n $localGUID ]]; then
if ! [[ $(diskutil apfs listcryptousers / | grep -c "$localGUID") -gt 0 ]]; then
sendToLog "Credential Error: Provided account \"$localOPTION\" is not a system volume owner."; credentialERROR="TRUE"
fi
localVALID=$(dscl /Local/Default -authonly "$localOPTION" "$localPASSWORD" 2>&1)
if ! [[ "$localVALID" == "" ]];then
sendToLog "Credential Error: The provided password for account \"$localOPTION\" is not valid."; credentialERROR="TRUE"
fi
else
sendToLog "Credential Error: Could not retrieve GUID for account \"$localOPTION\". Verify that account exists locally."; credentialERROR="TRUE"
fi
fi
# Validate that $adminACCOUNT and $adminPASSWORD are simultaneously provided.
if [[ -n $adminACCOUNT ]] && [[ -z $adminPASSWORD ]]; then
sendToLog "Credential Error: A local admin account name requires that you also set a local admin password."; credentialERROR="TRUE"
fi
if [[ -z $adminACCOUNT ]] && [[ -n $adminPASSWORD ]]; then
sendToLog "Credential Error: A local admin password requires that you also set a local admin account name."; credentialERROR="TRUE"
fi
# Validate that $adminACCOUNT is also specified with $superOPTION.
if [[ -n $superOPTION ]] && [[ -z $adminACCOUNT ]]; then
sendToLog "Credential Error: Local admin credentials are required to set a custom super service account name."; credentialERROR="TRUE"
fi
# Validate that $adminACCOUNT exists, is a volume owner, a local admin, and that $adminPASSWORD is correct.
if [[ -n $adminACCOUNT ]] && [[ "$credentialERROR" != "TRUE" ]]; then
adminGUID=$(dscl . read "/Users/$adminACCOUNT" GeneratedUID 2> /dev/null | awk '{print $2;}')
if [[ -n $adminGUID ]]; then
if [[ $(groups "$adminACCOUNT" | grep -c 'admin') -eq 0 ]]; then
sendToLog "Credential Error: Provided account \"$adminACCOUNT\" is not a local administrator."; credentialERROR="TRUE"
fi
if ! [[ $(diskutil apfs listcryptousers / | grep -c "$adminGUID") -gt 0 ]]; then
sendToLog "Credential Error: Provided account \"$adminACCOUNT\" is not a system volume owner."; credentialERROR="TRUE"
fi
adminVALID=$(dscl /Local/Default -authonly "$adminACCOUNT" "$adminPASSWORD" 2>&1)
if ! [[ "$adminVALID" == "" ]];then
sendToLog "Credential Error: The provided password for account \"$adminACCOUNT\" is not valid."; credentialERROR="TRUE"
fi
else
sendToLog "Credential Error: Could not retrieve GUID for account \"$adminACCOUNT\". Verify that account exists locally."; credentialERROR="TRUE"
fi
fi
# Validate that $jamfOPTION is only used on computers with the jamf binary installed.
if [[ -n $jamfOPTION ]] && [[ "$jamfVERSION" == "FALSE" ]]; then
sendToLog "Credential Error: A Jamf Pro API account name requires that this computer is enrolled in Jamf Pro."; credentialERROR="TRUE"
fi
# Validate that $jamfOPTION and $jamfPASSWORD are simultaneously provided.
if [[ -n $jamfOPTION ]] && [[ -z $jamfPASSWORD ]]; then
sendToLog "Credential Error: A Jamf Pro API account name requires that you also set a Jamf Pro API password."; credentialERROR="TRUE"
fi
if [[ -z $jamfOPTION ]] && [[ -n $jamfPASSWORD ]]; then
sendToLog "Credential Error: A Jamf Pro API password requires that you also set a Jamf Pro API account name."; credentialERROR="TRUE"
fi
# Validate that the account $jamfOPTION and $jamfPASSWORD are valid.
if [[ -n $jamfOPTION ]] && [[ "$credentialERROR" != "TRUE" ]]; then
jamfACCOUNT="$jamfOPTION"
jamfKEYCHAIN="$jamfPASSWORD"
if [[ "$jamfSERVER" != "FALSE" ]]; then
getJamfProAccount
[[ "$jamfERROR" == "TRUE" ]] && credentialERROR="TRUE"
else
sendToLog "Credential Error: Unable to connect to Jamf Pro to validate user account."; credentialERROR="TRUE"
fi
unset jamfACCOUNT
unset jamfKEYCHAIN
fi
# Collect any previously saved account names from $superPLIST.
localPROPERTY=$(defaults read "$superPLIST" LocalAccount 2> /dev/null)
superPROPERTY=$(defaults read "$superPLIST" SuperAccount 2> /dev/null)
jamfPROPERTY=$(defaults read "$superPLIST" JamfAccount 2> /dev/null)
# Some messaging to indicate if there are no saved accounts when a delete is requested.
{ [[ -z $localPROPERTY ]] && [[ -z $superPROPERTY ]] && [[ -z $jamfPROPERTY ]] && [[ -n $deleteACCOUNTS ]]; } && sendToLog "Startup: No saved accounts to delete."
# If there was a previous $localPROPERTY account and the user specified $localOPTION or $deleteACCOUNTS then delete any previously saved local account credentials.
if [[ -n $localPROPERTY ]] && { [[ -n $localOPTION ]] || [[ "$deleteACCOUNTS" == "TRUE" ]]; }; then
sendToLog "Startup: Deleting saved credentials for local account \"$localPROPERTY\"."
defaults delete "$superPLIST" LocalAccount > /dev/null 2>&1
security delete-generic-password -a "$localPROPERTY" -s "Super Local Account" /Library/Keychains/System.keychain > /dev/null 2>&1
unset localPROPERTY
localCREDENTIAL="FALSE"
fi
# If there was a previous $superPROPERTY account and the user specified $adminACCOUNT or $deleteACCOUNTS then delete any previously saved super service account and credentials.
if [[ -n $superPROPERTY ]] && { [[ -n $adminACCOUNT ]] || [[ "$deleteACCOUNTS" == "TRUE" ]]; } then
sendToLog "Startup: Deleting local account and saved credentials for super service account \"$superPROPERTY\"."
sysadminctl -deleteUser "$superPROPERTY" > /dev/null 2>&1
defaults delete "$superPLIST" SuperAccount > /dev/null 2>&1
security delete-generic-password -a "$superPROPERTY" -s "Super Service Account" /Library/Keychains/System.keychain > /dev/null 2>&1
unset superPROPERTY
superCREDENTIAL="FALSE"
fi
# If there was a previous $jamfPROPERTY account and the user specified $jamfOPTION or $deleteACCOUNTS then delete any previously saved Jamf Pro API credentials.
if [[ -n $jamfPROPERTY ]] && { [[ -n $jamfOPTION ]] || [[ "$deleteACCOUNTS" == "TRUE" ]]; } then
sendToLog "Startup: Deleting saved credentials for Jamf Pro API account \"$jamfPROPERTY\"."
defaults delete "$superPLIST" JamfAccount > /dev/null 2>&1
security delete-generic-password -a "$jamfPROPERTY" -s "Super MDM Account" /Library/Keychains/System.keychain > /dev/null 2>&1
unset jamfPROPERTY
jamfCREDENTIAL="FALSE"
fi
# If any previous validation generated a $credentialERROR then it's not necessary to continue this function.
[[ $credentialERROR == "TRUE" ]] && return 0
# If a new local account was specified, save $localOPTION and $localPASSWORD credentials and then validate retrieval.
if [[ -n $localOPTION ]]; then
sendToLog "Startup: Saving new credentials for local account \"$localOPTION\"..."
defaults write "$superPLIST" LocalAccount -string "$localOPTION"
localACCOUNT=$(defaults read "$superPLIST" LocalAccount 2> /dev/null)
if [[ "$localOPTION" == "$localACCOUNT" ]]; then
security add-generic-password -a "$localACCOUNT" -s "Super Local Account" -w "$localPASSWORD" -T /usr/bin/security /Library/Keychains/System.keychain
localKEYCHAIN=$(security find-generic-password -w -a "$localACCOUNT" -s "Super Local Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ "$localPASSWORD" == "$localKEYCHAIN" ]]; then
sendToLog "Startup: Validated saved credentials for local account \"$localACCOUNT\"."
localCREDENTIAL="TRUE"
else
sendToLog "Credential Error: Unable to validate saved password for local account \"$localACCOUNT\", deleting saved password."; credentialERROR="TRUE"
security delete-generic-password -a "$localACCOUNT" -s "Super Local Account" /Library/Keychains/System.keychain > /dev/null 2>&1
fi
else
sendToLog "Credential Error: Unable to validate saved name for local account \"$localOPTION\", deleting saved name."; credentialERROR="TRUE"
defaults delete "$superPLIST" LocalAccount > /dev/null 2>&1
fi
fi
# If an $adminACCOUNT was specified then a new super service account needs to be created and its credentials saved.
if [[ -n $adminACCOUNT ]]; then
# If the a custom super service account name is requested via $superOPTION.
if [[ -n $superOPTION ]]; then
superNEWACCT="$superOPTION"
superNEWFULL="$superOPTION"
else # Use the default names for the super service account.
superNEWACCT="super"
superNEWFULL="Super Update Service"
fi
# If a custom super service account password is requested via $superPASSWORD.
if [[ -n $superPASSWORD ]]; then
superNEWPASS="$superPASSWORD"
else # Use the default random password for the super service account.
superNEWPASS=$(uuidgen)
fi
# Save and validate new super service account credentials, and validate retrieval.
sendToLog "Startup: Saving new credentials for super service account \"$superNEWACCT\"..."
defaults write "$superPLIST" SuperAccount -string "$superNEWACCT"
superACCOUNT=$(defaults read "$superPLIST" SuperAccount 2> /dev/null)
if [[ "$superNEWACCT" == "$superACCOUNT" ]]; then
security add-generic-password -a "$superACCOUNT" -s "Super Service Account" -w "$superNEWPASS" -T /usr/bin/security /Library/Keychains/System.keychain
superKEYCHAIN=$(security find-generic-password -w -a "$superACCOUNT" -s "Super Service Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ "$superNEWPASS" == "$superKEYCHAIN" ]]; then # If saved credentials are valid then create the new super service account.
sendToLog "Startup: Validated saved credentials for new super service account \"$superACCOUNT\"."
if [[ $(id "$superACCOUNT" 2>&1 | grep -c 'no such user') -gt 0 ]]; then
sendToLog "Startup: Deleting existing super service account \"$superACCOUNT\" in preparation for new account."
sysadminctl -deleteUser "$superACCOUNT" > /dev/null 2>&1
fi
# Loop through local IDs to find the first vacant id after 500.
newUID=501
while [[ $(id $newUID 2>&1 | grep -c 'no such user') -eq 0 ]]; do
newUID=$((newUID + 1))
done
sendToLog "Startup: Creating new super service account \"$superNEWACCT\" with full name \"$superNEWFULL\" and UID $newUID..."
addRESULT=$(sysadminctl -addUser "$superNEWACCT" -fullName "$superNEWFULL" -password "$superNEWPASS" -UID $newUID -GID 20 -shell /dev/null -home /dev/null -picture "$cachedICON" -adminUser "$adminACCOUNT" -adminPassword "$adminPASSWORD" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: addRESULT is:\n$addRESULT"
dscl . create /Users/"$superNEWACCT" IsHidden 1
updateACCOUNT="$superACCOUNT"
updateKEYCHAIN="$superKEYCHAIN"
checkLocalUpdateAccount
if [[ "$accountERROR" != "TRUE" ]]; then
sendToLog "Startup: Validated the creation of new super service account \"$superACCOUNT\"."
superCREDENTIAL="TRUE"
else
sendToLog "Credential Error: Unable to validate newly created super service account \"$superACCOUNT\", deleting account"; credentialERROR="TRUE"
sysadminctl -deleteUser "$superACCOUNT" > /dev/null 2>&1
defaults delete "$superPLIST" SuperAccount > /dev/null 2>&1
security delete-generic-password -a "$superACCOUNT" -s "Super Service Account" /Library/Keychains/System.keychain > /dev/null 2>&1
unset superPROPERTY
fi
else
sendToLog "Credential Error: Unable to validate saved password for new super service account \"$superNEWACCT\", deleting saved password."; credentialERROR="TRUE"
security delete-generic-password -a "$superACCOUNT" -s "Super Service Account" /Library/Keychains/System.keychain > /dev/null 2>&1
fi
else
sendToLog "Credential Error: Unable to validate saved name for new super service account \"$superNEWACCT\",deleting saved name."; credentialERROR="TRUE"
defaults delete "$superPLIST" SuperAccount > /dev/null 2>&1
fi
fi
# If a new Jamf Pro API account was specified, save $jamfOPTION and $jamfPASSWORD credentials and then validate retrieval.
if [[ -n $jamfOPTION ]]; then
sendToLog "Startup: Saving new credentials for Jamf Pro API account \"$jamfOPTION\"..."
defaults write "$superPLIST" JamfAccount -string "$jamfOPTION"
jamfACCOUNT=$(defaults read "$superPLIST" JamfAccount 2> /dev/null)
if [[ "$jamfOPTION" == "$jamfACCOUNT" ]]; then
security add-generic-password -a "$jamfACCOUNT" -s "Super MDM Account" -w "$jamfPASSWORD" -T /usr/bin/security /Library/Keychains/System.keychain
jamfKEYCHAIN=$(security find-generic-password -w -a "$jamfACCOUNT" -s "Super MDM Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ "$jamfPASSWORD" == "$jamfKEYCHAIN" ]]; then
sendToLog "Startup: Validated saved credentials for Jamf Pro API account \"$jamfACCOUNT\"."
jamfCREDENTIAL="TRUE"
else
sendToLog "Credential Error: Unable to validate saved password for Jamf Pro API account \"$jamfACCOUNT\", deleting saved password."; credentialERROR="TRUE"
security delete-generic-password -a "$jamfACCOUNT" -s "Super MDM Account" /Library/Keychains/System.keychain > /dev/null 2>&1
fi
else
sendToLog "Credential Error: Unable to validate saved name for Jamf Pro API account \"$jamfOPTION\", deleting saved name."; credentialERROR="TRUE"
defaults delete "$superPLIST" JamfAccount > /dev/null 2>&1
fi
fi
# If there is a previously saved local account (that wasn't just deleted), validate the account and set $localACCOUNT and $localPASSWORD.
if [[ -n $localPROPERTY ]]; then
localACCOUNT="$localPROPERTY"
localKEYCHAIN=$(security find-generic-password -w -a "$localACCOUNT" -s "Super Local Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ -n $localKEYCHAIN ]]; then
sendToLog "Startup: Found saved credentials for local account \"$localACCOUNT\"."
updateACCOUNT="$localACCOUNT"
updateKEYCHAIN="$localKEYCHAIN"
checkLocalUpdateAccount
if [[ "$accountERROR" != "TRUE" ]]; then
sendToLog "Startup: Validated saved credentials for local account \"$localACCOUNT\"."
localCREDENTIAL="TRUE"
else
sendToLog "Credential Error: Unable to validate saved credentials for local account \"$localACCOUNT\"."; credentialERROR="TRUE"
fi
else
sendToLog "Credential Error: Unable to retrieve password for saved local account \"$localACCOUNT\"."; credentialERROR="TRUE"
fi
fi
# If there is a previously saved super service account (that wasn't just deleted), validate the account and set $superACCOUNT and $superPASSWORD.
if [[ -n $superPROPERTY ]]; then
superACCOUNT="$superPROPERTY"
superKEYCHAIN=$(security find-generic-password -w -a "$superACCOUNT" -s "Super Service Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ -n $superKEYCHAIN ]]; then
sendToLog "Startup: Found saved credentials for super service account \"$superACCOUNT\"."
updateACCOUNT="$superACCOUNT"
updateKEYCHAIN="$superKEYCHAIN"
checkLocalUpdateAccount
if [[ "$accountERROR" != "TRUE" ]]; then
sendToLog "Startup: Validated saved credentials for super service account \"$superACCOUNT\"."
superCREDENTIAL="TRUE"
else
sendToLog "Credential Error: Unable to validate saved credentials for super service account \"$superACCOUNT\"."; credentialERROR="TRUE"
fi
else
sendToLog "Credential Error: Unable to retrieve password for saved super service account \"$superACCOUNT\"."; credentialERROR="TRUE"
fi
fi
# If there is a previously saved Jamf PRO API account (that wasn't just deleted), validate the account and set $jamfACCOUNT and $jamfPASSWORD.
if [[ -n $jamfPROPERTY ]]; then
jamfACCOUNT="$jamfPROPERTY"
jamfKEYCHAIN=$(security find-generic-password -w -a "$jamfACCOUNT" -s "Super MDM Account" /Library/Keychains/System.keychain 2> /dev/null)
if [[ -n $jamfKEYCHAIN ]]; then
sendToLog "Startup: Found saved credentials for Jamf Pro API account \"$jamfACCOUNT\"."
if [[ "$jamfSERVER" != "FALSE" ]]; then
getJamfProAccount
if [[ "$jamfERROR" != "TRUE" ]]; then
sendToLog "Startup: Validated saved credentials for Jamf Pro API account \"$jamfACCOUNT\"."
jamfCREDENTIAL="TRUE"
else
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverNOSERVICE" == "TRUE" ]] || { [[ "$installNowOPTION" == "TRUE" ]] && [[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]]; }; then
sendToLog "Warning: Unable to validate Jamf Pro user account, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
elif [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to validate Jamf Pro user account, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to validate Jamf Pro user account, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to validate Jamf Pro user account, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to validate Jamf Pro user account, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
else
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverNOSERVICE" == "TRUE" ]] || { [[ "$installNowOPTION" == "TRUE" ]] && [[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]]; }; then
sendToLog "Warning: Unable to connect to Jamf Pro to validate user account, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
elif [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to connect to Jamf Pro to validate user account, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to connect to Jamf Pro to validate user account, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to connect to Jamf Pro to validate user account, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to connect to Jamf Pro to validate user account, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
else
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverNOSERVICE" == "TRUE" ]] || { [[ "$installNowOPTION" == "TRUE" ]] && [[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]]; }; then
sendToLog "Warning: Unable to retrieve password for saved Jamf Pro API account \"$jamfACCOUNT\", failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
else
sendToLog "Credential Error: Unable to retrieve password for saved Jamf Pro API account \"$jamfACCOUNT\"."; credentialERROR="TRUE"
fi
fi
fi
}
# This function determines what $updateWORKFLOW, $upgradeWORKFLOW, and betaWORKFLOW modes are possible given the architecture and authentication options.
manageWorkflowOptions() {
workflowERROR="FALSE"
# Update/upgrade workflow modes: FALSE, JAMF, LOCAL, or USER
updateWORKFLOW="FALSE"
upgradeWORKFLOW="FALSE"
betaWORKFLOW="FALSE"
# Logic to determin update/upgrade workflow.
if [[ "$macOSARCH" == "arm64" ]]; then # Mac computers with Apple Silicon.
sendToLog "Startup: Apple Silicon Mac computer running $(sw_vers -productName) $(sw_vers -productVersion)-$(sw_vers -buildVersion)."
sendToLog "Startup: Last macOS startup was $lastREBOOT."
if [[ "$localCREDENTIAL" == "TRUE" ]]; then
sendToLog "Startup: macOS update/upgrade workflows authenticated via local account."
installACCOUNT="$localACCOUNT"
installPASSWORD="$localKEYCHAIN"
updateWORKFLOW="LOCAL"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="LOCAL"
elif [[ "$superCREDENTIAL" == "TRUE" ]]; then
sendToLog "Startup: macOS update/upgrade workflows authenticated via super service account."
installACCOUNT="$superACCOUNT"
installPASSWORD="$superKEYCHAIN"
updateWORKFLOW="LOCAL"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="LOCAL"
elif [[ "$jamfCREDENTIAL" == "TRUE" ]]; then
if [[ $macOSVERSION -ge 1103 ]]; then
if [[ -n $userAuthMDMFailoverOPTION ]]; then
sendToLog "Startup: macOS update/upgrade workflows authenticated via Jamf Pro API with user authentication failover types: $userAuthMDMFailoverOPTION."
else
sendToLog "Startup: macOS update/upgrade workflows authenticated via Jamf Pro API with no user authentication MDM failover."
fi
updateWORKFLOW="JAMF"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="JAMF"
else
sendToLog "Warning: Automatic macOS update/upgrade enforcement via MDM is only available on macOS 11.3 or newer."
sendToLog "Startup: User authentication is required to perform a macOS update/upgrade."
updateWORKFLOW="USER"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="USER"
fi
else
sendToLog "Warning: Automatic macOS update/upgrade enforcement on Apple Silicon computers requires authentication credentials."
sendToLog "Startup: User authentication is required to perform a macOS update/upgrade."
updateWORKFLOW="USER"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="USER"
fi
else # Mac computers with Intel.
sendToLog "Startup: Intel Mac computer running macOS $(sw_vers -productVersion)-$(sw_vers -buildVersion)."
sendToLog "Startup: Last macOS startup was $lastREBOOT."
sendToLog "Startup: macOS update/upgrade workflows via system account (root)."
updateWORKFLOW="LOCAL"
[[ "$allowUpgradeOPTION" == "TRUE" ]] && upgradeWORKFLOW="LOCAL"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: updateWORKFLOW is: $updateWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: upgradeWORKFLOW is: $upgradeWORKFLOW"
# Check for beta program enrollment.
if [[ $macOSVERSION -ge 1304 ]]; then
queryDeviceINFO=$(/usr/libexec/mdmclient QueryDeviceInformation 2> /dev/null | grep 'IsDefaultCatalog')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: queryDeviceINFO is:\n$queryDeviceINFO"
if [[ $(echo "$queryDeviceINFO" | grep -c '1') -eq 0 ]]; then
sendToLog "Startup: This system is currently configured to receive macOS beta updates and upgrades."
betaWORKFLOW="TRUE"
fi
else # macOS versions prior to 13.4.
seedutilSTATUS=$(/System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil current)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: seedutilSTATUS is:\n$seedutilSTATUS"
if [[ $(echo "$seedutilSTATUS" | grep -c 'Is Enrolled: YES') -gt 0 ]]; then
sendToLog "Startup: This system is currently configured to receive macOS beta updates and upgrades."
betaWORKFLOW="TRUE"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: betaWORKFLOW is: $betaWORKFLOW"
}
# MARK: *** Installation & Startup ***
################################################################################
# Download and install the IBM Notifier.app.
getIbmNotifier() {
sendToLog "Startup: Attempting to download and install IBM Notifier.app..."
downloadRESULT=$(curl "$ibmNotifierURL" -L -o "/tmp/IBM.Notifier.zip" 2>&1)
if [[ -f "/tmp/IBM.Notifier.zip" ]]; then
unzipRESULT=$(unzip "/tmp/IBM.Notifier.zip" -d "$superFOLDER/" 2>&1)
if [[ -d "$ibmNotifierAPP" ]]; then
[[ -d "$superFOLDER/__MACOSX" ]] && rm -Rf "$superFOLDER/__MACOSX" > /dev/null 2>&1
chmod -R a+rx "$ibmNotifierAPP"
ibmNotifierVALID="TRUE"
rm -Rf "/tmp/IBM.Notifier.zip"
else
sendToLog "Error: Unable to install IBM Notifier.app."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: unzipRESULT is:\n$unzipRESULT"
fi
else
sendToLog "Error: Unable to download IBM Notifier.app from: $ibmNotifierURL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: downloadRESULT is:\n$downloadRESULT"
fi
}
# Check the IBM Notifier.app for validity.
checkIbmNotifier() {
ibmNotifierVALID="FALSE"
ibmNotifierCODESIGN=$(codesign --verify --verbose "$ibmNotifierAPP" 2>&1)
if [[ $(echo "$ibmNotifierCODESIGN" | grep -c 'valid on disk') -gt 0 ]]; then
ibmNotifierRESULT=$(defaults read "$ibmNotifierAPP/Contents/Info.plist" CFBundleShortVersionString)
if [[ "$ibmNotifierVERSION" == "$ibmNotifierRESULT" ]]; then
ibmNotifierVALID="TRUE"
else
sendToLog "Warning: IBM Notifier at path: $ibmNotifierAPP is version $ibmNotifierRESULT, this does not match target version $ibmNotifierVERSION"
fi
else
sendToLog "Warning: unable validate signature for IBM Notifier at path: $ibmNotifierAPP."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierCODESIGN is:\n$ibmNotifierCODESIGN"
fi
}
# Download and install the erase-install.pkg.
getEraseInstall() {
sendToLog "Startup: Attempting to download and install erase-install.pkg..."
downloadRESULT=$(curl "$eraseInstallURL" -L -o "/tmp/erase-install.pkg" 2>&1)
if [[ -f "/tmp/erase-install.pkg" ]]; then
installRESULT=$(installer -verboseR -pkg "/tmp/erase-install.pkg" -target / 2>&1)
if [[ $(echo "$installRESULT" | grep -c 'installer:PHASE:The software was successfully installed.') -gt 0 ]]; then
eraseInstallVALID="TRUE"
rm -Rf "/tmp/erase-install.pkg"
else
sendToLog "Error: Unable to install erase-install.pkg."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: installRESULT is:\n$installRESULT"
fi
else
sendToLog "Error: Unable to download erase-install.pkg from: $eraseInstallURL."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: downloadRESULT is:\n$downloadRESULT"
fi
}
# Check all erase-install items for validity.
checkEraseInstall() {
eraseInstallVALID="FALSE"
eraseInstallRESULT=$(grep -w "version=" "$eraseInstallSCRIPT" | awk -F '"' '{print $2}')
if [[ "$eraseInstallVERSION" == "$eraseInstallRESULT" ]]; then
eraseInstallSHASUM=$(echo "$eraseInstallCHECKSUM $eraseInstallSCRIPT" | shasum -c 2>&1)
if [[ $(echo "$eraseInstallSHASUM" | grep -c 'OK') -gt 0 ]]; then
eraseInstallVALID="TRUE"
else
sendToLog "Warning: Unable validate checksum for erase-install.sh at path: $eraseInstallSCRIPT."
[[ "$eraseInstallSHASUM" == "TRUE" ]] && sendToLog "Verbose Mode: eraseInstallSHASUM is:\n$eraseInstallSHASUM"
fi
else
sendToLog "Warning: erase-install.sh at path: $eraseInstallSCRIPT is version $eraseInstallRESULT, this does not match target version $eraseInstallVERSION."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: eraseInstallRESULT is:\n$eraseInstallRESULT"
fi
if [[ "$eraseInstallVALID" == "TRUE" ]] && [[ ! -e "$installInstallMacOS" ]]; then
sendToLog "Warning: Unable to locate installinstallmacOS.py at path: $installInstallMacOS"
eraseInstallVALID="FALSE"
fi
if [[ "$eraseInstallVALID" == "TRUE" ]] && [[ ! -d "$pythonFRAMEWORK" ]]; then
sendToLog "Warning: Unable to locate Python.framework at path: $pythonFRAMEWORK"
eraseInstallVALID="FALSE"
fi
}
# Install and validate helper items that may be used by super.
manageHelpers() {
helperERROR="FALSE"
# Validate $jamfBINARY if installed and set $jamfVERSION and $jamfSERVER accordingly.
jamfVERSION="FALSE"
if [[ -e "$jamfBINARY" ]]; then
getJamfProServer
jamfMAJOR=$("$jamfBINARY" -version | cut -c 9- | cut -d'.' -f1) # Expected output: 10
jamfMINOR=$("$jamfBINARY" -version | cut -c 9- | cut -d'.' -f2) # Expected output: 30, 31, 32, etc.
jamfVERSION=${jamfMAJOR}$(printf "%02d" "$jamfMINOR") # Expected output: 1030, 1031, 1032, etc.
if [[ $macOSVERSION -ge 1103 ]] && [[ $jamfVERSION -lt 1038 ]]; then
sendToLog "Helper Error: super requires Jamf Pro version 10.38 or later, the currently installed version of Jamf Pro $jamfVERSION is not supported."; helperERROR="TRUE"
elif [[ "$jamfVERSION" -lt 1000 ]]; then
sendToLog "Helper Error: super requires Jamf Pro version 10.00 or later, the currently installed version of Jamf Pro $jamfVERSION is not supported."; helperERROR="TRUE"
else
sendToLog "Startup: Computer is currently managed by Jamf Pro version $jamfMAJOR.$jamfMINOR."
fi
else
sendToLog "Startup: Unable to locate jamf binary at: $jamfBINARY"
fi
# Manage $preferJamfHelperOPTION and save to $superPLIST.
if [[ -n $preferJamfHelperOPTION ]]; then
if [[ $preferJamfHelperOPTION -eq 1 ]] || [[ "$preferJamfHelperOPTION" == "TRUE" ]]; then
if [[ "$jamfVERSION" != "FALSE" ]]; then
preferJamfHelperOPTION="TRUE"
defaults write "$superPLIST" PreferJamfHelper -bool true
else
sendToLog "Helper Error: No local Jamf binary found, thus can not prefer jamfHelper."; helperERROR="TRUE"
fi
else
preferJamfHelperOPTION="FALSE"
defaults delete "$superPLIST" PreferJamfHelper 2> /dev/null
fi
fi
# If needed, validate the IBM Notifier.app, if missing or invalid then install and check again.
if [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
ibmNotifierVALID="FALSE"
if [[ $macOSMAJOR -ge 11 ]] || [[ $macOSVERSION -ge 1015 ]]; then
if [[ ! -d "$ibmNotifierAPP" ]]; then
getIbmNotifier
[[ -d "$ibmNotifierAPP" ]] && checkIbmNotifier
[[ "$ibmNotifierVALID" == "FALSE" ]] && sendToLog "Error: Unable to validate IBM Notifier.app after installation, attempting fallback to jamfHelper."
else
checkIbmNotifier
if [[ "$ibmNotifierVALID" == "FALSE" ]]; then
sendToLog "Startup: Removing existing IBM Notifier.app."
rm -Rf "$ibmNotifierAPP" > /dev/null 2>&1
[[ -d "$superFOLDER/__MACOSX" ]] && rm -Rf "$superFOLDER/__MACOSX" > /dev/null 2>&1
getIbmNotifier
[[ -d "$ibmNotifierAPP" ]] && checkIbmNotifier
fi
[[ "$ibmNotifierVALID" == "FALSE" ]] && sendToLog "Error: Unable to validate IBM Notifier.app after re-installation, attempting fallback to jamfHelper."
fi
else
sendToLog "Warning: IBM Notifier.app is not compatible with this version of macOS, attempting fallback to jamfHelper."
fi
else
[[ "$helperERROR" == "FALSE" ]] && sendToLog "Startup: Prefer jamfHelper mode enabled."
fi
# If jamfHelper is to be used, then validate $jamfHelper and show some logs.
if [[ "$preferJamfHelperOPTION" == "TRUE" ]] || [[ "$ibmNotifierVALID" == "FALSE" ]]; then
if [[ ! -e "$jamfHELPER" ]]; then
sendToLog "Helper Error: Cannot locate fallback jamfHelper at: $jamfHELPER"; helperERROR="TRUE"
fi
sendToLog "Warning: Showing the deferral time inside the Defer button is not available when using jamfHelper."
{ [[ -n $displayAccessoryTypeOPTION ]] || [[ -n $displayAccessoryContentOPTION ]]; } && sendToLog "Warning: The the custom display accessory option is not available when using jamfHelper."
[[ -n $helpBUTTON ]] && sendToLog "Warning: The help button option is not available when using jamfHelper."
[[ -n $warningBUTTON ]] && sendToLog "Warning: The warning button option is not available when using jamfHelper."
[[ "$displaySilentlyOPTION" == "TRUE" ]] && sendToLog "Warning: The display silently option is not available when using jamfHelper."
fi
# This workflow error needs to go here because it requires the helpers are validated.
if [[ "$macOSARCH" == "arm64" ]] && { [[ "$upgradeWORKFLOW" == "USER" ]] || [[ "$updateWORKFLOW" == "USER" ]] || [[ -n $userAuthMDMFailoverOPTION ]]; } && { [[ "$preferJamfHelperOPTION" == "TRUE" ]] || [[ "$ibmNotifierVALID" == "FALSE" ]]; }; then
sendToLog "Workflow Error: User authenticated workflows requires use of IBM Notifier.app"; workflowERROR="TRUE"
fi
# If needed, validate erase-install items, if missing or invalid then install and check again.
if [[ "$upgradeWORKFLOW" != "FALSE" ]]; then
eraseInstallVALID="FALSE"
if [[ ! -d "$eraseInstallFOLDER" ]]; then
getEraseInstall
[[ -d "$eraseInstallFOLDER" ]] && checkEraseInstall
[[ "$eraseInstallVALID" == "FALSE" ]] && sendToLog "Error: Unable to validate erase-install items after installation, can not upgrade macOS."
else
checkEraseInstall
if [[ "$eraseInstallVALID" == "FALSE" ]]; then
sendToLog "Startup: Removing existing erase-install items."
rm -Rf "$eraseInstallFOLDER" > /dev/null 2>&1
getEraseInstall
[[ -d "$eraseInstallFOLDER" ]] && checkEraseInstall
fi
[[ "$eraseInstallVALID" == "FALSE" ]] && sendToLog "Error: Unable to validate erase-install items after re-installation, can not upgrade macOS."
fi
[[ "$eraseInstallVALID" == "FALSE" ]] && helperERROR="TRUE"
fi
}
# Install items required by super.
superInstallation() {
# Figure out where super is running from and start Installation log if anything needs to be installed.
superPATH="$(dirname "$0")"
[[ ! -d "$superFOLDER" ]] && mkdir -p "$superFOLDER"
# Install super if it's running from any location that is not in the $superFOLDER or from the $superLINK.
if ! { [[ "$superPATH" == "$superFOLDER" ]] || [[ "$superPATH" == "$(dirname "$superLINK")" ]]; }; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALLATION ****"
sendToLog "Installation: Copying file: $superFOLDER/super"
cp "$0" "$superFOLDER/super" > /dev/null 2>&1
if [[ ! -d "/usr/local/bin" ]]; then
sendToLog "Installation: Creating local search path: /usr/local/bin"
mkdir -p "/usr/local/bin"
chmod -R a+rx "/usr/local/bin"
fi
sendToLog "Installation: Creating search path link: $superLINK"
ln -s "$superFOLDER/super" "$superLINK" > /dev/null 2>&1
sendToLog "Installation: Creating LaunchDaemon helper: $superFOLDER/super-starter"
/bin/cat < "$superFOLDER/super-starter"
#!/bin/sh
echo "\$(date +"%a %b %d %T") \$(hostname -s) \$(basename "\$0")[\$\$]: **** S.U.P.E.R.M.A.N. $superVERSION LAUNCHDAEMON ****" | tee -a "$superLOG"
"$superFOLDER/super" "\$@" &
disown -a
exit 0
EOSS
touch "$asuListLOG"
touch "$installerListLOG"
touch "$asuLOG"
touch "$installerLOG"
touch "$mdmCommandLOG"
touch "$mdmWorkflowLOG"
sendToLog "Installation: Setting permissions in: $superFOLDER"
chown -R root:wheel "$superFOLDER"
chmod -R a+r "$superFOLDER"
chmod a+x "$superFOLDER/super"
chmod a+x "$superFOLDER/super-starter"
chown root:wheel "$superLINK"
chmod a+rx "$superLINK"
fi
}
# Prepare super by cleaning after previous super runs, record various maintenance modes, validate parameters, and liberate super from Jamf Policy runs.
superStartup() {
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION STARTUP ****"
sendToStatus "Running: Startup workflow."
sendToPending "Currently running."
# If super is running after an update/upgrade restart and set $restartVALIDATE appropriately.
if [[ -f "$restartValidateFilePATH" ]]; then
rm -f "$restartValidateFilePATH" 2> /dev/null
restartVALIDATE="TRUE"
defaults delete "$superPLIST" RestartValidate 2> /dev/null # This line is only here to remove any legacy RestartValidate keys.
defaults delete "$superPLIST" InstallNow 2> /dev/null
defaults delete "$superPLIST" SoftwareUpdatesList 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpgradeLabel 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpgradeTitle 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeName 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeVersion 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpdateDownloadLabel 2> /dev/null
defaults delete "$superPLIST" LastReboot 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
restartZeroDay
restartDeferralCounters
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Local preference file after restart validation $superPLIST is:\n$(defaults read "$superPLIST" 2> /dev/null)"
fi
# Collect any locally cached or managed preferences.
getPreferences
# Check for any previous super process still running, if so kill it.
if [[ -f "$superPIDFILE" ]]; then
previousPID=$(cat "$superPIDFILE")
sendToLog "Startup: Found previous super instance running with PID $previousPID, killing..."
kill -9 "$previousPID" > /dev/null 2>&1
fi
# Kill any already running helper processes.
killall -9 "softwareupdate" > /dev/null 2>&1
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
killall -9 "jamfHelper" > /dev/null 2>&1
# Create $superPIDFILE for this instance of super.
echo $$ > "$superPIDFILE"
# This unloads and deletes any previous LaunchDaemons.
if [[ -f "/Library/LaunchDaemons/$launchDaemonNAME.plist" ]]; then
sendToLog "Startup: Removing previous LaunchDaemon $launchDaemonNAME.plist."
launchctl bootout system "/Library/LaunchDaemons/$launchDaemonNAME.plist" 2> /dev/null
rm -f "/Library/LaunchDaemons/$launchDaemonNAME.plist"
fi
# Manage the $verboseModeOPTION and if enabled start additional logging.
if [[ $verboseModeOPTION -eq 1 ]] || [[ "$verboseModeOPTION" == "TRUE" ]]; then
verboseModeOPTION="TRUE"
defaults write "$superPLIST" VerboseMode -bool true
else
verboseModeOPTION="FALSE"
defaults delete "$superPLIST" VerboseMode 2> /dev/null
fi
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Verbose mode enabled."
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Uptime is: $(uptime)"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Managed preference file $superMANAGEDPLIST is:\n$(defaults read "$superMANAGEDPLIST" 2> /dev/null)"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Local preference file before validation $superPLIST is:\n$(defaults read "$superPLIST" 2> /dev/null)"
fi
# Main parameter validation and management.
checkCurrentUser
manageParameters
# Workflow for for $openLOGS.
if [[ "$openLOGS" == "TRUE" ]]; then
if [[ "$currentUserNAME" != "FALSE" ]]; then
sendToLog "Startup: Opening logs for user \"$currentUserNAME\"."
if [[ "$macOSARCH" == "arm64" ]]; then
sudo -u "$currentUserNAME" open "$mdmWorkflowLOG"
sudo -u "$currentUserNAME" open "$mdmCommandLOG"
fi
sudo -u "$currentUserNAME" open "$installerLOG"
sudo -u "$currentUserNAME" open "$asuLOG"
sudo -u "$currentUserNAME" open "$installerListLOG"
sudo -u "$currentUserNAME" open "$asuListLOG"
sudo -u "$currentUserNAME" open "$superLOG"
else
sendToLog "Startup: Open logs request denied because there is currently no local user logged into the GUI."
fi
fi
# Additional validation and management.
[[ "$macOSARCH" == "arm64" ]] && manageUpdateCredentials
manageWorkflowOptions
manageHelpers
[[ "$verboseModeOPTION" == "TRUE" ]] && logParameters
if [[ "$parameterERROR" == "TRUE" ]] || [[ "$credentialERROR" == "TRUE" ]] || [[ "$workflowERROR" == "TRUE" ]] || [[ "$helperERROR" == "TRUE" ]]; then
sendToLog "Exit: Startup validation failed."
sendToStatus "Inactive Error: Startup validation failed."
errorExit
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Local preference file after all validations $superPLIST is:\n$(defaults read "$superPLIST" 2> /dev/null)"
# If super is running via Jamf, then restart via LaunchDaemon to release the jamf process.
# This is late in the startup workflow so as to only create a valid LaunchDaemon after parameter validation and housekeeping.
if [[ $1 == "/" ]] || [[ $(ps -p "$PPID" | awk '{print $4;}' | grep -c -i 'jamf') -gt 0 ]] || [[ $(ps -p "$PPID" | awk '{print $6;}' | grep -c -i 'jamf') -gt 0 ]]; then
sendToLog "Startup: Found that Jamf is installing or is the parent process, restarting with new LaunchDaemon..."
sendToStatus "Pending: Found that Jamf is installing or is the parent process, restarting with new LaunchDaemon."
makeLaunchDaemonRestartNow
fi
# If super is running from outside the $superFOLDER, then restart via LaunchDaemon to release any parent installer process.
if ! { [[ "$superPATH" == "$superFOLDER" ]] || [[ "$superPATH" == "$(dirname "$superLINK")" ]]; }; then
sendToLog "Startup: Found that super is installing, restarting with new LaunchDaemon..."
sendToStatus "Pending: Found that super is installing, restarting with new LaunchDaemon."
makeLaunchDaemonRestartNow
fi
# Wait for a valid network connection. If there is still no network after two minutes, an automatic deferral is started.
networkTIMEOUT=0
while [[ $(ifconfig -a inet 2>/dev/null | sed -n -e '/127.0.0.1/d' -e '/0.0.0.0/d' -e '/inet/p' | wc -l) -le 0 ]] && [[ $networkTIMEOUT -lt 120 ]]; do
sendToLog "Startup: Waiting for network..."
sleep 5
networkTIMEOUT=$((networkTIMEOUT + 5))
done
if [[ $(ifconfig -a inet 2>/dev/null | sed -n -e '/127.0.0.1/d' -e '/0.0.0.0/d' -e '/inet/p' | wc -l) -le 0 ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Network unavailable, install now workflow can not continue."
sendToStatus "Inactive Error: Network unavailable, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Network unavailable, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Network unavailable, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
# With startup complete, create a fail-safe startup LaunchDaemon in case the system is restarted (via the user or something else) wile super is active.
sendToLog "Startup: Creating fail-safe startup LaunchDaemon."
makeLaunchDaemonOnStartup
defaults write "$superPLIST" FailSafeActive -bool true
}
# This function is used when the super workflow exits with no errors.
cleanExit() {
[[ -n "$jamfProTOKEN" ]] && deleteJamfProServerToken
defaults delete "$superPLIST" InstallNow 2> /dev/null
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Local preference file $superPLIST is:\n$(defaults read "$superPLIST" 2> /dev/null)"
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION EXIT ****"
rm -f "$superPIDFILE"
exit 0
}
# This function is used when the super workflow must exit due to an unrecoverable error.
errorExit() {
[[ -n "$jamfProTOKEN" ]] && deleteJamfProServerToken
defaults delete "$superPLIST" InstallNow 2> /dev/null
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Local preference file $superPLIST is:\n$(defaults read "$superPLIST" 2> /dev/null)"
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION ERROR EXIT ****"
sendToPending "Inactive."
rm -f "$superPIDFILE"
exit 1
}
# MARK: *** Logging ***
################################################################################
# Append input to the command line and log located at $superLOG.
sendToLog() {
echo -e "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: $*" | tee -a "$superLOG"
}
# Send input to the command line only, so as not to save secrets to the $superLOG.
sendToEcho() {
echo -e "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: Not Logged: $*"
}
# Send input to the command line only replacing the current line, so as not to save save interactive progress updates to the $superLOG.
sendToEchoReplaceLine() {
echo -ne "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: Not Logged: $*\r"
}
# Append input to a log located at $asuLOG.
sendToASULog() {
echo -e "\n$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: $*" >> "$asuLOG"
}
# Append input to a log located at $installerLOG.
sendToInstallerLog() {
echo -e "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: $*" >> "$installerLOG"
}
# Append input to a log located at $mdmCommandLOG.
sendToMDMCommandLog() {
echo -e "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: $*" >> "$mdmCommandLOG"
}
# Append input to a log located at $mdmWorkflowLOG.
sendToMDMWorkflowLog() {
echo -e "$(date +"%a %b %d %T") $(hostname -s) $(basename "$0")[$$]: $*" >> "$mdmWorkflowLOG"
}
# Update the SuperStatus key in the $superPLIST.
sendToStatus() {
defaults write "$superPLIST" SuperStatus -string "$(date +"%a %b %d %T"): $*"
}
# Update the SuperPending key in the $superPLIST.
sendToPending() {
defaults write "$superPLIST" SuperPending -string "$*"
}
# Log any parameters that have values.
logParameters() {
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: superVERSION is: $superVERSION"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: superDATE is: $superDATE"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSMAJOR is: $macOSMAJOR"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSMINOR is: $macOSMINOR"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSVERSION is: $macOSVERSION"
[[ "$macOSMAJOR" -ge 13 ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSEXTRA is: $macOSEXTRA"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSARCH is: $macOSARCH"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macModelID is: $macModelID"
[[ -n $macBOOK ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macBOOK is: $macBOOK"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: lastREBOOT is: $lastREBOOT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: parameterERROR is: $parameterERROR"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: credentialERROR is: $credentialERROR"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowERROR is: $workflowERROR"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: helperERROR is: $helperERROR"
[[ -n $jamfVERSION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfVERSION is: $jamfVERSION"
[[ -n $jamfSERVER ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfSERVER is: $jamfSERVER"
[[ -n $ibmNotifierVALID ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierVALID is: $ibmNotifierVALID"
[[ -n $eraseInstallVALID ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: eraseInstallVALID is: $eraseInstallVALID"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: defaultDeferSECONDS is: $defaultDeferSECONDS"
[[ -n $focusDeferSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDeferSECONDS is: $focusDeferSECONDS"
[[ -n $menuDeferSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: menuDeferSECONDS is: $menuDeferSECONDS"
[[ -n $recheckDeferSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: recheckDeferSECONDS is: $recheckDeferSECONDS"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: errorDeferSECONDS is: $errorDeferSECONDS"
[[ -n $focusCountMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusCountMAX is: $focusCountMAX"
[[ -n $softCountMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softCountMAX is: $softCountMAX"
[[ -n $hardCountMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: hardCountMAX is: $hardCountMAX"
[[ -n $focusDaysMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDaysMAX is: $focusDaysMAX"
[[ -n $softDaysMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softDaysMAX is: $softDaysMAX"
[[ -n $hardDaysMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: hardDaysMAX is: $hardDaysMAX"
[[ -n $zeroDayOVERRIDE ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayOVERRIDE is: $zeroDayOVERRIDE"
[[ -n $focusDateMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDateMAX is: $focusDateMAX"
[[ -n $softDateMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softDateMAX is: $softDateMAX"
[[ -n $hardDateMAX ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: hardDateMAX is: $hardDateMAX"
[[ -n $deferDialogTimeoutSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deferDialogTimeoutSECONDS is: $deferDialogTimeoutSECONDS"
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softDialogTimeoutSECONDS is: $softDialogTimeoutSECONDS"
[[ -n $displayRedrawSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayRedrawSECONDS is: $displayRedrawSECONDS"
[[ -n $ibmNotifierIconSIZE ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierIconSIZE is: $ibmNotifierIconSIZE"
[[ -n $jamfHelperIconSIZE ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperIconSIZE is: $jamfHelperIconSIZE"
[[ -n $displayAccessoryTYPE ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryTYPE is: $displayAccessoryTYPE"
[[ -n $displayAccessoryDefaultCONTENT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryDefaultCONTENT is: $displayAccessoryDefaultCONTENT"
[[ -n $displayAccessoryUpdateCONTENT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryUpdateCONTENT is: $displayAccessoryUpdateCONTENT"
[[ -n $displayAccessoryUpgradeCONTENT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryUpgradeCONTENT is: $displayAccessoryUpgradeCONTENT"
[[ -n $displayAccessoryUserAuthCONTENT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryUserAuthCONTENT is: $displayAccessoryUserAuthCONTENT"
[[ -n $helpBUTTON ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: helpBUTTON is: $helpBUTTON"
[[ -n $warningBUTTON ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: warningBUTTON is: $warningBUTTON"
[[ -n $displaySilentlyOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displaySilentlyOPTION is: $displaySilentlyOPTION"
[[ -n $preferJamfHelperOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preferJamfHelperOPTION is: $preferJamfHelperOPTION"
[[ -n $localOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: localOPTION is: $localOPTION"
[[ -n $localPASSWORD ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: localPASSWORD is: $localPASSWORD"
[[ -n $localACCOUNT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: localACCOUNT is: $localACCOUNT"
[[ -n $localKEYCHAIN ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: localKEYCHAIN is: $localKEYCHAIN"
[[ -n $localCREDENTIAL ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: localCREDENTIAL is: $localCREDENTIAL"
[[ -n $adminACCOUNT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: adminACCOUNT is: $adminACCOUNT"
[[ -n $adminPASSWORD ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: adminPASSWORD is: $adminPASSWORD"
[[ -n $superOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: superOPTION is: $superOPTION"
[[ -n $superPASSWORD ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: superPASSWORD is: $superPASSWORD"
[[ -n $superACCOUNT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: superACCOUNT is: $superACCOUNT"
[[ -n $superKEYCHAIN ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: superKEYCHAIN is: $superKEYCHAIN"
[[ -n $superCREDENTIAL ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: superCREDENTIAL is: $superCREDENTIAL"
[[ -n $JamfProID ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: JamfProID is: $JamfProID"
[[ -n $jamfOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfOPTION is: $jamfOPTION"
[[ -n $jamfPASSWORD ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: jamfPASSWORD is: $jamfPASSWORD"
[[ -n $jamfACCOUNT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfACCOUNT is: $jamfACCOUNT"
[[ -n $jamfKEYCHAIN ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: jamfKEYCHAIN is: $jamfKEYCHAIN"
[[ -n $jamfCREDENTIAL ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfCREDENTIAL is: $jamfCREDENTIAL"
[[ -n $deleteACCOUNTS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deleteACCOUNTS is: $deleteACCOUNTS"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthTimeoutSECONDS is: $userAuthTimeoutSECONDS"
[[ -n $userAuthMDMFailoverOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFailoverOPTION is: $userAuthMDMFailoverOPTION"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFAILOVER is: $userAuthMDMFAILOVER"
[[ -n $userAuthMDMFailoverSOFT ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFailoverSOFT is: $userAuthMDMFailoverSOFT"
[[ -n $userAuthMDMFailoverHARD ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFailoverHARD is: $userAuthMDMFailoverHARD"
[[ -n $userAuthMDMFailoverINSTALLNOW ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFailoverINSTALLNOW is: $userAuthMDMFailoverINSTALLNOW"
[[ -n $userAuthMDMFailoverBOOTSTRAP ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFailoverBOOTSTRAP is: $userAuthMDMFailoverBOOTSTRAP"
[[ -n $allowUpgradeOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: allowUpgradeOPTION is: $allowUpgradeOPTION"
[[ -n $targetUpgradeVERSION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: targetUpgradeVERSION is: $targetUpgradeVERSION"
[[ -n $allowRSRUpdatesOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: allowRSRUpdatesOPTION is: $allowRSRUpdatesOPTION"
[[ -n $enforceNonSystemUpdatesOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: enforceNonSystemUpdatesOPTION is: $enforceNonSystemUpdatesOPTION"
[[ -n $onlyDownloadOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: onlyDownloadOPTION is: $onlyDownloadOPTION"
[[ -n $installNowOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: installNowOPTION is: $installNowOPTION"
[[ -n $policyTRIGGERS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: policyTRIGGERS is: $policyTRIGGERS"
[[ -n $skipUpdatesOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: skipUpdatesOPTION is: $skipUpdatesOPTION"
[[ -n $restartWithoutUpdatesOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: restartWithoutUpdatesOPTION is: $restartWithoutUpdatesOPTION"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: freeSpaceUpdateGB is: $freeSpaceUpdateGB"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: freeSpaceUpgradeGB is: $freeSpaceUpgradeGB"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: freeSpaceTimeoutSECONDS is: $freeSpaceTimeoutSECONDS"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: batteryLevelPERCENT is: $batteryLevelPERCENT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: batteryTimeoutSECONDS is: $batteryTimeoutSECONDS"
[[ -n $testModeOPTION ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: testModeOPTION is: $testModeOPTION"
[[ -n $testModeTimeoutSECONDS ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: testModeTimeoutSECONDS is: $testModeTimeoutSECONDS"
}
# MARK: *** Jamf Pro API ***
################################################################################
# Validate the connection to a managed computer's Jamf Pro service and set $jamfSERVER accordingly.
getJamfProServer() {
jamfSTATUS=$("$jamfBINARY" checkJSSConnection -retry 1 2>/dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfSTATUS is: $jamfSTATUS"
if [[ $(echo "$jamfSTATUS" | grep -c 'available') -gt 0 ]]; then
jamfSERVER=$(defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url)
else
sendToLog "Warning: Jamf Pro service unavailable."; jamfSERVER="FALSE"; jamfERROR="TRUE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfSTATUS is: $jamfSTATUS"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfSERVER is: $jamfSERVER"
}
# Attempt to acquire a Jamf Pro $jamfProTOKEN via $jamfACCOUNT and $jamfKEYCHAIN credentials.
getJamfProToken() {
getJamfProServer
if [[ "$jamfSERVER" != "FALSE" ]]; then
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfACCOUNT is: $jamfACCOUNT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: jamfKEYCHAIN is: $jamfKEYCHAIN"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfSERVER is: $jamfSERVER"
commandRESULT=$(curl -X POST -u "$jamfACCOUNT:$jamfKEYCHAIN" -s "${jamfSERVER}api/v1/auth/token")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is:\n$commandRESULT"
if [[ $(echo "$commandRESULT" | grep -c 'token') -gt 0 ]]; then
if [[ $macOSMAJOR -ge 12 ]]; then
jamfProTOKEN=$(echo "$commandRESULT" | plutil -extract token raw -)
else
jamfProTOKEN=$(echo "$commandRESULT" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
fi
else
sendToLog "Error: Response from Jamf Pro API token request did not contain a token."; jamfERROR="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfProTOKEN is:\n$jamfProTOKEN"
fi
}
# Validate that the account $jamfACCOUNT and $jamfKEYCHAIN are valid credentials and has appropriate permissions to send MDM push commands. If not set $jamfERROR.
getJamfProAccount() {
getJamfProToken
if [[ -n $jamfProTOKEN ]]; then
getJamfProComputerID
if [[ -n $jamfProID ]]; then
sendBlankPush
if [[ $commandRESULT != 201 ]]; then
sendToLog "Error: Unable to request Blank Push via Jamf Pro API user account \"$jamfACCOUNT\". Verify this account has has the privileges \"Jamf Pro Server Objects > Computers > Create & Read\"."; jamfERROR="TRUE"
fi
else
sendToLog "Error: Unable to acquire Jamf Pro ID for computer with UDID \"$computerUDID\". Verify that this computer is enrolled in Jamf Pro."
sendToLog "Error: Also verify that the Jamf Pro API account \"$jamfACCOUNT\" has the privileges \"Jamf Pro Server Objects > Computers > Create & Read\"."; jamfERROR="TRUE"
fi
else
sendToLog "Error: Unable to acquire authentication token via Jamf Pro API user account \"$jamfACCOUNT\". Verify account name and password."; jamfERROR="TRUE"
fi
}
# Use $jamfProIdMANAGED or $jamfProTOKEN to find the computer's Jamf Pro ID and set $jamfProID.
getJamfProComputerID() {
computerUDID=$(system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: computerUDID is: $computerUDID"
if [[ -n $jamfProIdMANAGED ]]; then
jamfProID="$jamfProIdMANAGED"
else
sendToLog "Warning: Using a Jamf Pro API account with \"Computers > Read\" privileges to collect the computer ID is a security risk. Instead use a custom Configuration Profile with the following; Preference Domain \"com.macjutsu.super\", Key \"JamfProID\", String \"\$JSSID\"."
jamfProID=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --header "Accept: application/xml" --request GET --url "${jamfSERVER}JSSResource/computers/udid/${computerUDID}/subset/General" 2> /dev/null | xpath -e /computer/general/id 2>&1 | awk -F '|' '{print $2}' | xargs)
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfProID is: $jamfProID"
}
# Attempt to send a Blank Push to Jamf Pro.
sendBlankPush() {
commandRESULT=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --write-out "%{http_code}" --silent --output /dev/null --request POST --url "${jamfSERVER}JSSResource/computercommands/command/BlankPush/id/${jamfProID}")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is:\n$commandRESULT"
}
# Validate existing $jamfProTOKEN and if found invalid, a new token is requested and again validated.
checkJamfProServerToken() {
tokenCHECK=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --write-out "%{http_code}" --silent --output /dev/null --request GET --url "${jamfSERVER}api/v1/auth")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: tokenCHECK is: $tokenCHECK"
if [[ $tokenCHECK -ne 200 ]]; then
getJamfProToken
tokenCHECK=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --write-out "%{http_code}" --silent --output /dev/null --request GET --url "${jamfSERVER}api/v1/auth")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: tokenCHECK is: $tokenCHECK"
if [[ $tokenCHECK -ne 200 ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Could not request Jamf Pro API token for account \"$jamfACCOUNT\", install now workflow can not continue."
sendToStatus "Inactive Error: Could not request Jamf Pro API token for account \"$jamfACCOUNT\", install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Could not request Jamf Pro API token for account \"$jamfACCOUNT\", trying again in $deferSECONDS seconds."
sendToStatus "Pending: Could not request Jamf Pro API token for account \"$jamfACCOUNT\", trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
}
# Invalidate and remove from local memory the $jamfProTOKEN.
deleteJamfProServerToken() {
invalidateTOKEN=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --write-out "%{http_code}" --silent --output /dev/null --request POST --url "${jamfSERVER}api/v1/auth/invalidate-token")
if [[ $invalidateTOKEN -eq 204 ]]; then
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Jamf Pro API token successfully invalidated."
unset jamfProTOKEN
elif [[ $invalidateTOKEN -eq 401 ]]; then
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: Jamf Pro API token already invalid."
unset jamfProTOKEN
else
sendToLog "Error: Invalidating Jamf Pro API token."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: invalidateTOKEN is: $invalidateTOKEN"
fi
}
# MARK: *** Local System Validation ***
################################################################################
# Verify that super is running with root privileges.
checkRoot() {
if [[ "$(id -u)" -ne 0 ]]; then
sendToEcho "Exit: $(basename "$0") must run with root privileges."
errorExit
fi
}
# Set $currentUserNAME to the currently logged in GUI user or "FALSE" if there is none or a system account.
# If the current user is a normal account then this also sets $currentUserUID, $currentUserGUID, $currentUserRealNAME, $currentUserADMIN, $currentUserSecureTOKEN, and $currentUserVolumeOWNER
checkCurrentUser() {
currentUserNAME=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ { print $3 }')
if [[ -z $currentUserNAME ]]; then
sendToLog "Status: No GUI user currently logged in."
currentUserNAME="FALSE"
currentUserUID="FALSE"
elif [[ "$currentUserNAME" = "root" ]] || [[ "$currentUserNAME" = "_mbsetupuser" ]] || [[ "$currentUserNAME" = "loginwindow" ]]; then
sendToLog "Status: Current GUI user is system account \"$currentUserNAME\"."
currentUserNAME="FALSE"
currentUserUID="0"
else
sendToLog "Status: Current GUI user name is \"$currentUserNAME\"."
fi
if [[ "$currentUserNAME" != "FALSE" ]]; then
currentUserUID=$(id -u "$currentUserNAME" 2> /dev/null)
currentUserGUID=$(dscl . read "/Users/$currentUserNAME" GeneratedUID 2> /dev/null | awk '{print $2;}')
currentUserRealNAME=$(dscl . read "/Users/$currentUserNAME" RealName 2> /dev/null | tail -1 | sed -e 's/RealName: //g' | xargs)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserUID is: $currentUserUID"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserGUID is: $currentUserGUID"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserRealNAME is: $currentUserRealNAME"
if [[ -n $currentUserUID ]] && [[ -n $currentUserGUID ]] && [[ -n $currentUserRealNAME ]]; then
if [[ $(groups "$currentUserNAME" 2> /dev/null | grep -c 'admin') -gt 0 ]]; then
currentUserADMIN="TRUE"
else
currentUserADMIN="FALSE"
fi
if [[ $(dscl . read "/Users/$currentUserNAME" AuthenticationAuthority 2> /dev/null | grep -c 'SecureToken') -gt 0 ]]; then
currentUserSecureTOKEN="TRUE"
else
currentUserSecureTOKEN="FALSE"
fi
if [[ $(diskutil apfs listcryptousers / 2> /dev/null | grep -c "$currentUserGUID") -gt 0 ]]; then
currentUserVolumeOWNER="TRUE"
else
currentUserVolumeOWNER="FALSE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserADMIN is: $currentUserADMIN"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserSecureTOKEN is: $currentUserSecureTOKEN"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentUserVolumeOWNER is: $currentUserVolumeOWNER"
else
sendToLog "Parameter Error: Unable to determine account details for user \"$currentUserNAME\"."; parameterERROR="TRUE"
fi
fi
}
# Validate that the account $updateACCOUNT and $updateKEYCHAIN are valid credentials is a volume owner. If not set $accountERROR.
checkLocalUpdateAccount() {
accountGUID=$(dscl . read "/Users/$updateACCOUNT" GeneratedUID 2> /dev/null | awk '{print $2;}')
if [[ -n $accountGUID ]]; then
if ! [[ $(diskutil apfs listcryptousers / | grep -c "$accountGUID") -gt 0 ]]; then
sendToLog "Error: Provided account \"$updateACCOUNT\" is not a system volume owner."; accountERROR="TRUE"
fi
accountVALID=$(dscl /Local/Default -authonly "$updateACCOUNT" "$updateKEYCHAIN" 2>&1)
if ! [[ "$accountVALID" == "" ]];then
sendToLog "Error: The provided password for account \"$updateACCOUNT\" is not valid."; accountERROR="TRUE"
fi
else
sendToLog "Error: Could not retrieve GUID for account \"$updateACCOUNT\". Verify that account exists locally."; accountERROR="TRUE"
fi
}
# Collect the available free storage and set $storageREADY accordingly. This also sets $availableStorageGB and $requiredStorageGB.
checkAvailableStorage() {
storageREADY="FALSE"
[[ -z $currentUserNAME ]] && checkCurrentUser
[[ "$currentUserNAME" != "FALSE" ]] && availableStorageGB=$(osascript -l 'JavaScript' -e "ObjC.import('Foundation'); var freeSpaceBytesRef=Ref(); $.NSURL.fileURLWithPath('/').getResourceValueForKeyError(freeSpaceBytesRef, 'NSURLVolumeAvailableCapacityForImportantUsageKey', null); Math.round(ObjC.unwrap(freeSpaceBytesRef[0]) / 1000000000)")
[[ "$currentUserNAME" == "FALSE" ]] && availableStorageGB=$(/usr/libexec/mdmclient QueryDeviceInformation 2> /dev/null | grep AvailableDeviceCapacity | head -n 1 | awk '{print $3}' | sed -e 's/;//g' -e 's/"//g' -e 's/\.[0-9]*//g')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: availableStorageGB: $availableStorageGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS: $softwareUpdateMACOS"
if [[ -z $availableStorageGB ]] || [[ ! $availableStorageGB =~ $regexNUMBER ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to determine available free storage, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to determine available free storage, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to determine available free storage, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to determine available free storage, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
elif [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]] || [[ "$softwareUpdateMACOS" == "TRUE" ]]; then
{ [[ -z $freeSpaceUpdateOPTION ]] && [[ $macOSSoftwareUpdateGB -gt 5 ]]; } && freeSpaceUpdateGB=$((macOSSoftwareUpdateGB*2))
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerDownloadREQUIRED: $macOSInstallerDownloadREQUIRED"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateDownloadREQUIRED: $macOSSoftwareUpdateDownloadREQUIRED"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: freeSpaceUpgradeGB: $freeSpaceUpgradeGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeGB: $macOSSoftwareUpgradeGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerGB: $macOSInstallerGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: freeSpaceUpdateGB: $freeSpaceUpdateGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateGB: $macOSSoftwareUpdateGB"
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ "$upgradeWORKFLOW" == "LOCAL" ]] || [[ "$upgradeWORKFLOW" == "USER" ]]; then
if [[ $macOSVERSION -ge 1203 ]]; then # macOS 12.3 or newer upgrade via softwareupdate.
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then
requiredStorageGB=$((freeSpaceUpgradeGB+macOSSoftwareUpgradeGB))
else # Download calculation is not required.
requiredStorageGB=$freeSpaceUpgradeGB
fi
else # Systems older than macOS 12.3 upgrade via installer.
if [[ "$macOSInstallerDownloadREQUIRED" == "TRUE" ]]; then
requiredStorageGB=$((freeSpaceUpgradeGB+macOSInstallerGB))
else # Download calculation is not required.
requiredStorageGB=$freeSpaceUpgradeGB
fi
fi
elif [[ "$upgradeWORKFLOW" == "JAMF" ]]; then # MDM upgrade workflow via installer.
if [[ "$macOSInstallerDownloadREQUIRED" == "TRUE" ]]; then
requiredStorageGB=$((freeSpaceUpgradeGB+macOSInstallerGB))
else # Download calculation is not required.
requiredStorageGB=$freeSpaceUpgradeGB
fi
fi
else # macOS updates are available.
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then
requiredStorageGB=$((freeSpaceUpdateGB+macOSSoftwareUpdateGB))
else # Download calculation is not required.
requiredStorageGB=$freeSpaceUpdateGB
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: requiredStorageGB: $requiredStorageGB"
[[ $availableStorageGB -ge $requiredStorageGB ]] && storageREADY="TRUE"
else # No macOS update/upgrade is available.
storageREADY="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: storageREADY: $storageREADY"
}
# Validate if current system power is adequate for performing a macOS update/upgrade and set $powerREADY accordingly. Desktops, obviously, always return that they are ready.
checkAvailablePower() {
powerREADY="FALSE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macBOOK: $macBOOK"
if [[ "$macBOOK" == "TRUE" ]]; then
[[ $(pmset -g ps | grep -ic 'AC Power') -ne 0 ]] && acPOWER="TRUE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: acPOWER: $acPOWER"
if [[ "$acPOWER" == "TRUE" ]]; then
powerREADY="TRUE"
else # Not plugged into AC power.
currentBatteryLEVEL=$(pmset -g ps | grep '%' | awk '{print $3}' | sed -e 's/%;//g')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: currentBatteryLEVEL: $currentBatteryLEVEL"
if [[ -z $currentBatteryLEVEL ]] || [[ ! $currentBatteryLEVEL =~ $regexNUMBER ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to determine battery power level, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to determine battery power level, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to determine battery power level, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to determine available free storage, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
else # Battery level is a real number.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: batteryLevelPERCENT: $batteryLevelPERCENT"
[[ $currentBatteryLEVEL -gt $batteryLevelPERCENT ]] && powerREADY="TRUE"
fi
fi
else # Mac desktop.
powerREADY="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: powerREADY: $powerREADY"
}
# Validate the computer's MDM service status and set $mdmENROLLED, $mdmDEP, and $mdmSERVICE
checkMDMService() {
mdmENROLLED="FALSE"
mdmDEP="FALSE"
mdmSERVICE="FALSE"
profilesRESULT=$(profiles status -type enrollment 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: profilesRESULT:\n$profilesRESULT"
if [[ $(echo "$profilesRESULT" | grep -c 'MDM server') -gt 0 ]]; then
mdmENROLLED="TRUE"
[[ $(echo "$profilesRESULT" | grep 'Enrolled via DEP:' | grep -c 'Yes') -gt 0 ]] && mdmDEP="TRUE"
mdmSERVICE="https://$(echo "$profilesRESULT" | grep 'MDM server' | awk -F '/' '{print $3}')"
curlRESULT=$(curl -Is "$mdmSERVICE" | head -n 1)
if [[ $(echo "$curlRESULT" | grep -c 'HTTP') -gt 0 ]] && [[ $(echo "$curlRESULT" | grep -c -e '400' -e '40[4-9]' -e '4[1-9][0-9]' -e '5[0-9][0-9]') -eq 0 ]]; then
sendToLog "Status: MDM service is currently available at: $mdmSERVICE"
else
sendToLog "Warning: MDM service at $mdmSERVICE is currently unavailable with stauts: $curlRESULT"
mdmSERVICE="FALSE"
fi
else
sendToLog "Warning: System is not enrolled with a MDM service."
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: mdmENROLLED: $mdmENROLLED"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: mdmDEP: $mdmDEP"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: mdmSERVICE: $mdmSERVICE"
}
# Validate that the computer's bootstrap token is properly escrowed and set $bootstrapTOKEN.
checkBootstrapToken() {
bootstrapTOKEN="FALSE"
profilesRESULT=$(profiles status -type bootstraptoken 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: profilesRESULT:\n$profilesRESULT"
if [[ $(echo "$profilesRESULT" | grep -c 'YES') -eq 2 ]]; then
if [[ "$macOSVERSION" -ge 1303 ]]; then
if [[ "$mdmSERVICE" != "FALSE" ]]; then
queryDeviceINFO=$(/usr/libexec/mdmclient QueryDeviceInformation 2> /dev/null | grep 'EACSPreflight' | sed -e 's/ EACSPreflight = //g' -e 's/"//g' -e 's/;//g')
if [[ $(echo "$queryDeviceINFO" | grep -c 'success') -gt 0 ]] || [[ $(echo "$queryDeviceINFO" | grep -c 'EFI password exists') -gt 0 ]]; then
sendToLog "Status: Bootstrap token escrowed and validated with MDM service."
bootstrapTOKEN="TRUE"
else
sendToLog "Warning: Bootstrap token escrow validation failed with status: $queryDeviceINFO"
fi
else
sendToLog "Warning: Bootstrap token was previously escrowed with MDM service but the service is currently unavailable so it can not be validated."
fi
else
sendToLog "Status: Bootstrap token escrowed with MDM service."
bootstrapTOKEN="TRUE"
fi
else
sendToLog "Warning: Bootstrap token is not escrowed with MDM service."
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: bootstrapTOKEN: $bootstrapTOKEN"
}
# MARK: *** Deferrals & Deadlines Prep ***
################################################################################
# Delete the maximum deferral counters in $superPLIST to restart the counters.
restartZeroDay() {
sendToLog "Status: Restarting automatically set zero day date."
defaults delete "$superPLIST" ZeroDayAuto 2> /dev/null
}
# Delete the maximum deferral counters in $superPLIST to restart the counters.
restartDeferralCounters() {
sendToLog "Status: Restarting maximum deferral counters."
defaults delete "$superPLIST" FocusCounter 2> /dev/null
defaults delete "$superPLIST" SoftCounter 2> /dev/null
defaults delete "$superPLIST" HardCounter 2> /dev/null
}
# Evaluate $zeroDayOVERRIDE and $zeroDayPREVIOUS, then set $zeroDaySTART, $zeroDayEPOCH, and $zeroDayDISPLAY accordingly.
checkZeroDay() {
if [[ -n $zeroDayOVERRIDE ]]; then # If there is a $zeroDayOVERRIDE then use that first.
zeroDaySTART="$zeroDayOVERRIDE"
sendToLog "Status: Using manually set zero day date of $zeroDaySTART."
else
zeroDayPREVIOUS=$(defaults read "$superPLIST" ZeroDayAuto 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayPREVIOUS: $zeroDayPREVIOUS"
if [[ -n $zeroDayPREVIOUS ]]; then # If there was a previously saved zero day date then use that.
zeroDaySTART="$zeroDayPREVIOUS"
sendToLog "Status: Using previously set automatic zero day date of $zeroDaySTART."
else # Otherwise this is a new zero day date, so save to $superPLIST.
zeroDaySTART=$(date +"%Y-%m-%d:%H:%M")
sendToLog "Status: Setting new automatic day zero date to $zeroDaySTART."
defaults write "$superPLIST" ZeroDayAuto -string "$zeroDaySTART"
fi
fi
zeroDayEPOCH=$(date -j -f "%Y-%m-%d:%H:%M" "$zeroDaySTART" +"%s")
zeroDayDATE=$(date -r "$zeroDayEPOCH" "$dateFORMAT")
zeroDayTIME=$(date -r "$zeroDayEPOCH" "$timeFORMAT" | sed 's/^ *//g')
if [[ $(date -r "$zeroDayEPOCH" "+%H:%M") == "00:00" ]]; then
zeroDayDISPLAY="$zeroDayDATE"
else
zeroDayDISPLAY="$zeroDayDATE - $zeroDayTIME"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayEPOCH: $zeroDayEPOCH"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayDATE: $zeroDayDATE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayTIME: $zeroDayTIME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: zeroDayDISPLAY: $zeroDayDISPLAY"
}
# Evaluate if a process has told the display to not sleep or the user has enabled Focus or Do Not Disturb, and set $focusDEFER accordingly.
checkUserFocus() {
focusDEFER="FALSE"
if [[ -n $focusDeferSECONDS ]]; then
if [[ $macOSMAJOR -eq 10 ]]; then
focusSTATUS=$(sudo -u "$currentUserNAME" defaults -currentHost read "/Users/$currentUserNAME/Library/Preferences/ByHost/com.apple.notificationcenterui" doNotDisturb 2>/dev/null)
elif [[ $macOSMAJOR -eq 11 ]]; then
focusSTATUS=$(plutil -extract dnd_prefs xml1 -o - "/Users/$currentUserNAME/Library/Preferences/com.apple.ncprefs.plist" | xmllint --xpath "string(//data)" - | base64 --decode | plutil -convert xml1 - -o - | grep -ic userPref)
else
focusSTATUS=$(plutil -extract data.0.storeAssertionRecords.0.assertionDetails.assertionDetailsModeIdentifier raw -o - "/Users/$currentUserNAME/Library/DoNotDisturb/DB/Assertions.json" | grep -ic com.apple.)
fi
if [[ $focusSTATUS -gt 0 ]]; then
sendToLog "Status: Focus or Do Not Disturb enabled for user \"$currentUserNAME\"."
focusDEFER="TRUE"
fi
oldIFS="$IFS"; IFS=$'\n'
displayASSERTIONS=($(pmset -g assertions | awk '/NoDisplaySleepAssertion | PreventUserIdleDisplaySleep/ && match($0,/\(.+\)/) && ! /coreaudiod/ {gsub(/^\ +/,"",$0); print};'))
if [[ -n ${displayASSERTIONS[*]} ]]; then
for assertionITEM in "${displayASSERTIONS[@]}"; do
sendToLog "Status: The following Display Sleep Assertions found: $(echo "${assertionITEM}" | awk -F ':' '{print $1}')"
done
focusDEFER="TRUE"
fi
IFS="$oldIFS"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDEFER is: $focusDEFER"
}
# Evaluate $focusDateMAX, $softDateMAX, and $hardDateMAX, then set $deadlineDateSTATUS, $deadlineDateEPOCH, and $deadlineDateDISPLAY accordingly.
checkDateDeadlines() {
deadlineDateSTATUS="FALSE"
if [[ -n $focusDateMAX ]]; then
if [[ $focusDateEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Focus date deadline of $focusDateMAX HAS passed."
deadlineDateSTATUS="FOCUS"
else
sendToLog "Status: Focus date deadline of $focusDateMAX NOT passed."
fi
fi
if [[ -n $softDateMAX ]]; then
if [[ $softDateEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Soft date deadline of $softDateMAX HAS passed."
deadlineDateSTATUS="SOFT"
else
sendToLog "Status: Soft date deadline of $softDateMAX NOT passed."
fi
fi
if [[ -n $hardDateMAX ]]; then
if [[ $hardDateEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Date deadline of $hardDateMAX HAS passed."
deadlineDateSTATUS="HARD"
else
sendToLog "Status: Date deadline of $hardDateMAX NOT passed."
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDateSTATUS is: $deadlineDateSTATUS"
# For display the $softDateMAX always results in a sooner date than the $hardDateMAX.
[[ -n $hardDateMAX ]] && deadlineDateEPOCH="$hardDateEPOCH"
[[ -n $softDateMAX ]] && deadlineDateEPOCH="$softDateEPOCH"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDateEPOCH is: $deadlineDateEPOCH"
if [[ -n $deadlineDateEPOCH ]]; then
deadlineDateDATE=$(date -r "$deadlineDateEPOCH" "$dateFORMAT")
deadlineDateTIME=$(date -r "$deadlineDateEPOCH" "$timeFORMAT" | sed 's/^ *//g')
if [[ $(date -r "$deadlineDateEPOCH" "+%H:%M") == "00:00" ]]; then
deadlineDateDISPLAY="$deadlineDateDATE"
else
deadlineDateDISPLAY="$deadlineDateDATE - $deadlineDateTIME"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDateDATE is: $deadlineDateDATE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDateTIME is: $deadlineDateTIME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDateDISPLAY is: $deadlineDateDISPLAY"
fi
}
# Evaluate $focusDaysMAX, $softDaysMAX, and $hardDaysMAX, then set $deadlineDaysSTATUS, $deadlineDaysEPOCH, and $deadlineDaysDISPLAY accordingly.
checkDaysDeadlines() {
deadlineDaysSTATUS="FALSE"
if [[ -n $focusDaysMAX ]]; then
focusDaysEPOCH=$((zeroDayEPOCH + focusDaysSECONDS))
focusDaysDATE=$(date -r "$focusDaysEPOCH" +%Y-%m-%d:%H:%M)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDaysEPOCH: $focusDaysEPOCH"
if [[ $focusDaysEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Focus days deadline of $focusDaysDATE ($focusDaysMAX day(s) after $zeroDaySTART) HAS passed."
deadlineDaysSTATUS="FOCUS"
else
sendToLog "Status: Focus days deadline of $focusDaysDATE ($focusDaysMAX day(s) after $zeroDaySTART) NOT passed."
fi
fi
if [[ -n $softDaysMAX ]]; then
softDaysEPOCH=$((zeroDayEPOCH + softDaysSECONDS))
softDaysDATE=$(date -r "$softDaysEPOCH" +%Y-%m-%d:%H:%M)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softDaysEPOCH: $softDaysEPOCH"
if [[ $softDaysEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Soft days deadline of $softDaysDATE ($softDaysMAX day(s) after $zeroDaySTART) HAS passed."
deadlineDaysSTATUS="SOFT"
else
sendToLog "Status: Soft days deadline of $softDaysDATE ($softDaysMAX day(s) after $zeroDaySTART) NOT passed."
fi
fi
if [[ -n $hardDaysMAX ]]; then
hardDaysEPOCH=$((zeroDayEPOCH + hardDaysSECONDS))
hardDaysDATE=$(date -r "$hardDaysEPOCH" +%Y-%m-%d:%H:%M)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: hardDaysEPOCH: $hardDaysEPOCH"
if [[ $hardDaysEPOCH -lt $(date +%s) ]]; then
sendToLog "Status: Days deadline of $hardDaysDATE ($hardDaysMAX day(s) after $zeroDaySTART) HAS passed."
deadlineDaysSTATUS="HARD"
else
sendToLog "Status: Days deadline of $hardDaysDATE ($hardDaysMAX day(s) after $zeroDaySTART) NOT passed."
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDaysSTATUS is: $deadlineDaysSTATUS"
# For display the $softDaysMAX always results in a sooner date than the $hardDaysMAX.
[[ -n $hardDaysMAX ]] && deadlineDaysEPOCH="$hardDaysEPOCH"
[[ -n $softDaysMAX ]] && deadlineDaysEPOCH="$softDaysEPOCH"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDaysEPOCH is: $deadlineDaysEPOCH"
if [[ -n $deadlineDaysEPOCH ]]; then
deadlineDaysDATE=$(date -r "$deadlineDaysEPOCH" "$dateFORMAT")
deadlineDaysTIME=$(date -r "$deadlineDaysEPOCH" "$timeFORMAT" | sed 's/^ *//g')
if [[ $(date -r "$deadlineDaysEPOCH" "+%H:%M") == "00:00" ]]; then
deadlineDaysDISPLAY="$deadlineDaysDATE"
else
deadlineDaysDISPLAY="$deadlineDaysDATE - $deadlineDaysTIME"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDaysDATE is: $deadlineDaysDATE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDaysTIME is: $deadlineDaysTIME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDaysDISPLAY is: $deadlineDaysDISPLAY"
fi
# For display this sets $deadlineDISPLAY based on the soonest available date or days deadline.
if [[ -n $deadlineDateDISPLAY ]] && [[ -n $deadlineDaysDISPLAY ]]; then
if [[ $deadlineDateEPOCH -le $deadlineDaysEPOCH ]]; then
deadlineDISPLAY="$deadlineDateDISPLAY"
else
deadlineDISPLAY="$deadlineDaysDISPLAY"
fi
elif [[ -n $deadlineDateDISPLAY ]]; then
deadlineDISPLAY="$deadlineDateDISPLAY"
elif [[ -n $deadlineDaysDISPLAY ]]; then
deadlineDISPLAY="$deadlineDaysDISPLAY"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineDISPLAY is: $deadlineDISPLAY"
}
# Evaluate $focusCountMAX, $softCountMAX, and $hardCountMAX, then set $focusDEFER, $deadlineCountSTATUS, $countDISPLAY, and $countMaxDISPLAY accordingly.
checkCountDeadlines() {
deadlineCountSTATUS="FALSE"
if [[ "$focusDEFER" == "TRUE" ]]; then
if [[ -n $focusCountMAX ]]; then
focusCounterPREVIOUS=$(defaults read "$superPLIST" FocusCounter 2> /dev/null)
if [[ -z $focusCounterPREVIOUS ]]; then
sendToLog "Status: Creating new focus deferral counter in $superPLIST."
focusCounterCURRENT=0
defaults write "$superPLIST" FocusCounter -int $focusCounterCURRENT
else
focusCounterCURRENT=$((focusCounterPREVIOUS + 1))
defaults write "$superPLIST" FocusCounter -int $focusCounterCURRENT
fi
if [[ $focusCounterCURRENT -ge $focusCountMAX ]]; then
sendToLog "Status: Focus maximum deferral count of $focusCountMAX HAS passed."
deadlineCountSTATUS="FOCUS"
focusDEFER="FALSE"
else
focusCountDISPLAY=$((focusCountMAX - focusCounterCURRENT))
sendToLog "Status: Focus maximum deferral count of $focusCountMAX NOT passed with $focusCountDISPLAY remaining."
fi
else
sendToLog "Status: Focus or Do Not Disturb active, and no maximum focus deferral, so not incrementing deferral counters."
fi
fi
if [[ "$focusDEFER" == "FALSE" ]]; then
if [[ -n $softCountMAX ]]; then
softCounterPREVIOUS=$(defaults read "$superPLIST" SoftCounter 2> /dev/null)
if [[ -z $softCounterPREVIOUS ]]; then
sendToLog "Status: Creating new soft deferral counter in $superPLIST."
softCounterCURRENT=0
defaults write "$superPLIST" SoftCounter -int $softCounterCURRENT
else
softCounterCURRENT=$((softCounterPREVIOUS + 1))
defaults write "$superPLIST" SoftCounter -int $softCounterCURRENT
fi
if [[ $softCounterCURRENT -ge $softCountMAX ]]; then
sendToLog "Status: Soft maximum deferral count of $softCountMAX HAS passed."
deadlineCountSTATUS="SOFT"
else
softCountDISPLAY=$((softCountMAX - softCounterCURRENT))
sendToLog "Status: Soft maximum deferral count of $softCountMAX NOT passed with $softCountDISPLAY remaining."
fi
countDISPLAY="$softCountDISPLAY"
countMaxDISPLAY="$softCountMAX"
fi
if [[ -n $hardCountMAX ]]; then
hardCounterPREVIOUS=$(defaults read "$superPLIST" HardCounter 2> /dev/null)
if [[ -z $hardCounterPREVIOUS ]]; then
sendToLog "Status: Creating new hard deferral counter in $superPLIST."
hardCounterCURRENT=0
defaults write "$superPLIST" HardCounter -int $hardCounterCURRENT
else
hardCounterCURRENT=$((hardCounterPREVIOUS + 1))
defaults write "$superPLIST" HardCounter -int $hardCounterCURRENT
fi
if [[ $hardCounterCURRENT -ge $hardCountMAX ]]; then
sendToLog "Status: Hard maximum deferral count of $hardCountMAX HAS passed."
deadlineCountSTATUS="HARD"
else
hardCountDISPLAY=$((hardCountMAX - hardCounterCURRENT))
sendToLog "Status: Hard maximum deferral count of $hardCountMAX NOT passed with $hardCountDISPLAY remaining."
fi
countDISPLAY="$hardCountDISPLAY"
countMaxDISPLAY="$hardCountMAX"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deadlineCountSTATUS is: $deadlineCountSTATUS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: focusDEFER is: $focusDEFER"
}
# MARK: *** Update/Upgrade Prep ***
################################################################################
# This function checks the validity of recently run software updates/upgrade caches and sets $fullCheckREQUIRED, $softwareUpdatesAVAILABLE, and $macOSUpgradeAVAILABLE accordingly.
checkSoftwareListCache()
{
fullCheckREQUIRED="FALSE"
softwareUpdatesAVAILABLE="FALSE"
macOSUpgradeAVAILABLE="FALSE"
# Check to see if there was any previously available software updates information cached.
asuCheckDATE=$(defaults read "$asuPLIST" LastSuccessfulDate 2> /dev/null)
[[ -n $asuCheckDATE ]] && asuCheckEPOCH=$(date -j -u -f "%Y-%m-%d %H:%M:%S %z" "$asuCheckDATE" "+%s")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: asuCheckDATE is: $asuCheckDATE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: asuCheckEPOCH is: $asuCheckEPOCH"
if [[ $asuCheckEPOCH -gt $(($(date "+%s")-21600)) ]]; then
sendToLog "Status: Last automatic software update check was less than 6 hours ago."
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: $asuPLIST is:\n$(defaults read "$asuPLIST" 2> /dev/null)"
cacheSoftwareUpdatesAVAILABLE=$(defaults read "$asuPLIST" LastUpdatesAvailable 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: cacheSoftwareUpdatesAVAILABLE is: $cacheSoftwareUpdatesAVAILABLE"
if [[ $cacheSoftwareUpdatesAVAILABLE -gt 0 ]]; then # Software updates available. Evaluate previous update list and compare them to currently available updates, setting $fullCheckREQUIRED, $softwareUpdateLIST, and $softwareUpdatesAVAILABLE accordingly.
previousSoftwareUpdatesPROPERTY=$(defaults read "$superPLIST" SoftwareUpdatesList 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousSoftwareUpdatesPROPERTY is:\n$previousSoftwareUpdatesPROPERTY"
if [[ -n $previousSoftwareUpdatesPROPERTY ]] && [[ "$previousSoftwareUpdatesPROPERTY" != "0" ]]; then
previousSoftwareUpdatesLIST=$(echo "$previousSoftwareUpdatesPROPERTY" | tail -n +2 | sed -e 's/ //g' -e 's/"//g' -e 's/",//g' -e 's/,//g' -e '$d' | sort)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousSoftwareUpdatesLIST is:\n$previousSoftwareUpdatesLIST"
cacheSoftwareUpdatesLIST=$(defaults read "$asuPLIST" RecommendedUpdates | grep 'Identifier' | sed -e 's/ Identifier = //g' -e 's/"//g' -e 's/;//g' | sort)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: cacheSoftwareUpdatesLIST is:\n$cacheSoftwareUpdatesLIST"
if [[ "$previousSoftwareUpdatesLIST" == "$cacheSoftwareUpdatesLIST" ]]; then # Previous update list matches current update list.
softwareUpdateLIST=$(<"$asuListLOG") 2> /dev/null
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateLIST is:\n$softwareUpdateLIST"
if [[ $(echo "$softwareUpdateLIST" | grep -c 'Software Update found') -eq 0 ]] || [[ $(echo "$softwareUpdateLIST" | grep -c 'No new software available.') -gt 1 ]]; then
sendToLog "Status: $asuListLOG does not contain any updates but it should, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
else
sendToLog "Status: Cached software update list appears to be accurate."
softwareUpdatesAVAILABLE="TRUE"
fi
else
sendToLog "Status: Cached software update list does not match the current list, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
else # No previously saved $cacheSoftwareUpdatesLIST to compare.
sendToLog "Status: No software update list cache, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
fi
else
sendToLog "Status: Last automatic software update check is older than 6 hours, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdatesAVAILABLE is: $softwareUpdatesAVAILABLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: fullCheckREQUIRED is: $fullCheckREQUIRED"
# Check to see if there was any previously available macOS upgrade information cached.
if [[ "$fullCheckREQUIRED" == "FALSE" ]]; then
previousMacOSSoftwareUpgradeLABEL=$(defaults read "$superPLIST" macOSSoftwareUpgradeLabel 2> /dev/null)
previousMacOSSoftwareUpgradeTITLE=$(defaults read "$superPLIST" macOSSoftwareUpgradeTitle 2> /dev/null)
previousMacOSInstallerUpgradeNAME=$(defaults read "$superPLIST" macOSUpgradeName 2> /dev/null)
previousMacOSInstallerUpgradeVERSION=$(defaults read "$superPLIST" macOSUpgradeVersion 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousMacOSSoftwareUpgradeLABEL is: $previousMacOSSoftwareUpgradeLABEL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousMacOSSoftwareUpgradeTITLE is: $previousMacOSSoftwareUpgradeTITLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousMacOSInstallerUpgradeNAME is: $previousMacOSInstallerUpgradeNAME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousMacOSInstallerUpgradeVERSION is: $previousMacOSInstallerUpgradeVERSION"
if [[ $macOSVERSION -ge 1203 ]] && [[ -n $previousMacOSSoftwareUpgradeLABEL ]] && [[ -n $previousMacOSSoftwareUpgradeTITLE ]]; then
if [[ "$previousMacOSSoftwareUpgradeLABEL" != "0" ]] && [[ "$previousMacOSSoftwareUpgradeTITLE" != "0" ]]; then # macOS upgrade available per cache.
macOSUpgradeAVAILABLE="TRUE"
macOSSoftwareUpgradeLABEL="$previousMacOSSoftwareUpgradeLABEL"
macOSSoftwareUpgradeTITLE="$previousMacOSSoftwareUpgradeTITLE"
if [[ "$upgradeWORKFLOW" != "FALSE" ]]; then # macOS upgrade option is enabled.
if [[ -n $softwareUpdateLIST ]];then # Cached macOS installer list exists.
if [[ $(echo "$softwareUpdateLIST" | grep -c 'Software Update found') -gt 0 ]] && [[ $(echo "$softwareUpdateLIST" | grep -c 'macOS') -gt 0 ]];then # Cached macOS softwareupdate list was completed.
sendToLog "Status: Cached macOS upgrade list appears to be valid."
else
sendToLog "Status: macOS upgrade list cache invalid, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
else
sendToLog "Status: No macOS upgrade list cache, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
fi
fi
fi
if { [[ $macOSVERSION -ge 1015 ]] && [[ -n $previousMacOSInstallerUpgradeNAME ]] && [[ -n $previousMacOSInstallerUpgradeVERSION ]]; } || { [[ $macOSVERSION -lt 1015 ]] && [[ -n $previousMacOSInstallerUpgradeNAME ]]; }; then # Cached macOS upgrade name available.
if [[ "$previousMacOSInstallerUpgradeNAME" != "0" ]]; then # macOS upgrade available per cache.
macOSUpgradeAVAILABLE="TRUE"
macOSUpgradeNAME="$previousMacOSInstallerUpgradeNAME"
macOSUpgradeVERSION="$previousMacOSInstallerUpgradeVERSION"
macOSInstallerLIST=$(<"$installerListLOG") 2> /dev/null
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerLIST is:\n$macOSInstallerLIST"
if [[ "$upgradeWORKFLOW" != "FALSE" ]]; then # macOS upgrade option is enabled.
if [[ -n $macOSInstallerLIST ]];then # Cached macOS installer list exists.
if [[ $(echo "$macOSInstallerLIST" | grep -c 'finish') -gt 0 ]] && [[ $(echo "$macOSInstallerLIST" | grep -c 'exit code: 1') -eq 0 ]];then # Cached macOS installer list was completed.
sendToLog "Status: Cached macOS upgrade list appears to be valid."
else
sendToLog "Status: macOS upgrade list cache invalid, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
else
sendToLog "Status: No macOS upgrade list cache, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
fi
fi
else # No cached macOS upgrade name.
sendToLog "Status: No software upgrade status cache, full update/upgrade check required."
fullCheckREQUIRED="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeAVAILABLE is: $macOSUpgradeAVAILABLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: fullCheckREQUIRED is: $fullCheckREQUIRED"
fi
}
# This restarts various softwareupdate daemon processes.
kickSoftwareUpdateD() {
sendToLog "Status: Restarting various softwareupdate daemon processes..."
defaults delete /Library/Preferences/com.apple.Softwareupdate.plist > /dev/null 2>&1
if ! launchctl kickstart -k "system/com.apple.mobile.softwareupdated"; then
sendToLog "Warning: Restarting mobile softwareupdate daemon didn't respond, trying again in 10 seconds..."
sleep 10
launchctl kickstart -k "system/com.apple.mobile.softwareupdated"
fi
if ! launchctl kickstart -k "system/com.apple.softwareupdated"; then
sendToLog "Warning: Restarting system softwareupdate daemon didn't respond, trying again in 10 seconds..."
sleep 10
launchctl kickstart -k "system/com.apple.softwareupdated"
fi
# If a user is logged in then also restart the Software Update Notification Manager daemon.
if [[ "$currentUserNAME" != "FALSE" ]]; then
if ! launchctl kickstart -k "gui/$currentUserUID/com.apple.SoftwareUpdateNotificationManager"; then
sendToLog "Warning: Restarting Software Update Notification Manager didn't respond, trying again in 10 seconds..."
sleep 10
launchctl kickstart -k "gui/$currentUserUID/com.apple.SoftwareUpdateNotificationManager"
fi
fi
}
# This function checks for updates via softwareupdate and sets $softwareUpdatesAVAILABLE, $macOSSoftwareUpdatesAVAILABLE, and $softwareUpdateLIST accordingly. This is in a separate function to facilitate list caching and multiple run workflows.
listSoftwareUpdates() {
softwareUpdatesAVAILABLE="FALSE"
macOSSoftwareUpdatesAVAILABLE="FALSE"
# Background the softwareupdate checking process and send to $asuListLOG.
sudo -u root softwareupdate --list > "$asuListLOG" 2>&1 &
checkPID=$!
# Watch $asuListLOG while waiting for the softwareupdate list process to complete. Note this while read loop has a timeout based on $initialStartTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowStartTIMEOUT is: $workflowStartTIMEOUT"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c "Can’t connect" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c "Couldn't communicate" ) -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Finding available software') -gt 0 ]]; then
sendToLog "softwareupdate: Waiting for update list..."
elif [[ $(echo "$logLINE" | grep -c 'Software Update found') -gt 0 ]]; then
softwareUpdatesAVAILABLE="TRUE"
workflowStartFAIL="FALSE"
workflowStartTIMEOUT="FALSE"
wait $checkPID
break
elif [[ $(echo "$logLINE" | grep -c 'No new software available.') -gt 0 ]]; then
workflowStartFAIL="FALSE"
workflowStartTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$asuListLOG")
# If the softwareupdate list completed, then collect information.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]]; then
softwareUpdateLIST=$(<"$asuListLOG")
[[ $(echo "$softwareUpdateLIST" | grep -c 'macOS') -gt 0 ]] && macOSSoftwareUpdatesAVAILABLE="TRUE"
elif [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToLog "Error: softwareupdate check timed out after $workflowTimeoutSECONDS seconds."
kill -9 "$checkPID" > /dev/null 2>&1
kickSoftwareUpdateD
sleep 10
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToLog "Error: softwareupdate check failed, check $asuListLOG for more detail."
kill -9 "$checkPID" > /dev/null 2>&1
sleep 10
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdatesAVAILABLE is: $softwareUpdatesAVAILABLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdatesAVAILABLE is: $macOSSoftwareUpdatesAVAILABLE"
}
# This function evaluates software updates collected from the listSoftwareUpdates() function and sets super software update caches, $softwareUpdateMACOS, $softwareUpdateRSR, and $softwareUpdateRECOMMENDED accordingly.
# This also sets $allSoftwareUpdateLABELS[], $allSoftwareUpdateTITLES[], $macOSSoftwareUpdateLABELS[], $macOSSoftwareUpdateTITLES[], $macOSSoftwareUpdateVERSIONS[], $macOSSoftwareUpdateLABEL, $macOSSoftwareUpdateTITLE, $macOSSoftwareUpdateVERSION, $recommendedSoftwareUpdateLABELS[], and $recommendedSoftwareUpdateTITLES[].
checkSoftwareUpdates() {
softwareUpdateMACOS="FALSE"
softwareUpdateRSR="FALSE"
softwareUpdateRECOMMENDED="FALSE"
unset allSoftwareUpdateLABELS
unset allSoftwareUpdateTITLES
unset macOSSoftwareUpdateLABELS
unset macOSSoftwareUpdateTITLES
unset macOSSoftwareUpdateVERSIONS
unset macOSSoftwareUpdateGBS
unset macOSSoftwareUpdateLABEL
unset macOSSoftwareUpdateTITLE
unset macOSSoftwareUpdateVERSION
unset macOSSoftwareUpdateGB
unset recommendedSoftwareUpdateLABELS
unset recommendedSoftwareUpdateTITLES
# Only need to check if a full check is required.
if [[ "$fullCheckREQUIRED" == "TRUE" ]]; then
sendToLog "Status: Starting full check for software updates..."
listSoftwareUpdates
# Double-checking in case software update is misbehaving.
if [[ "$workflowStartFAIL" == "TRUE" ]] || [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToLog "Status: Re-checking for software updates..."
listSoftwareUpdates
elif [[ $macOSMAJOR -ge 11 ]] && [[ $macOSVERSION -lt 1303 ]] && { [[ "$softwareUpdatesAVAILABLE" == "FALSE" ]] || [[ "$macOSSoftwareUpdatesAVAILABLE" == "FALSE" ]]; }; then
sendToLog "Status: macOS 11.x - macOS 13.2, double-checking for software updates..."
kickSoftwareUpdateD
sleep 10
listSoftwareUpdates
fi
# Software update check failed, try again later.
if [[ "$workflowStartFAIL" == "TRUE" ]] || [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Checking for updates via softwareupdate did not complete after multiple attempts, install now workflow can not continue."
sendToStatus "Inactive Error: Checking for updates via softwareupdate did not complete after multiple attempts, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Checking for updates via softwareupdate did not complete after multiple attempts, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Checking for updates via softwareupdate did not complete after multiple attempts, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
# Extract software update information from from $asuPLIST and $asuListLOG.
if [[ "$softwareUpdatesAVAILABLE" == "TRUE" ]]; then
# Extract softare update list cache from $asuPLIST and save to $superPLIST. This is to be used in future runs of the checkSoftwareListCache() function.
cacheSoftwareUpdatesAVAILABLE=$(defaults read "$asuPLIST" LastUpdatesAvailable 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: cacheSoftwareUpdatesAVAILABLE is: $cacheSoftwareUpdatesAVAILABLE"
oldIFS="$IFS"; IFS=$'\n'
propertyUpdatesARRAY=($(defaults read "$asuPLIST" RecommendedUpdates | grep 'Identifier' | sed -e 's/ Identifier = //g' -e 's/"//g' -e 's/;//g'))
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: propertyUpdatesARRAY[] is:\n${propertyUpdatesARRAY[*]}"
defaults delete "$superPLIST" SoftwareUpdatesList 2> /dev/null
for i in "${!propertyUpdatesARRAY[@]}"; do
defaults write "$superPLIST" SoftwareUpdatesList -array-add "${propertyUpdatesARRAY[i]}"
done
# Parse $softwareUpdateLIST for software update labels, titles, and macOS updates.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateLIST is:\n$softwareUpdateLIST"
if [[ $macOSMAJOR -ge 12 ]]; then # For macOS 12 softwareupdate can list multiple macOS updates/upgrades.
allSoftwareUpdateLABELS=($(echo "$softwareUpdateLIST" | awk -F ': ' '/Label:/{print $2}'))
allSoftwareUpdateTITLES=($(echo "$softwareUpdateLIST" | awk -F ',' '/Title:/ {print $1}' | cut -d ' ' -f 2-))
macOSSoftwareUpdateLABELS=($(echo "$softwareUpdateLIST" | grep 'Label: macOS' | sed -e 's/* Label: //' | sort -k3 -r -V))
macOSSoftwareUpdateTITLES=($(echo "$softwareUpdateLIST" | grep 'Title: macOS' | sed -e 's/,/:/g' | awk -F ': ' '{print $2}' | sort -k3 -r -V))
macOSSoftwareUpdateVERSIONS=($(echo "$softwareUpdateLIST" | grep 'Title: macOS' | sed -e 's/,/:/g' | awk -F ': ' '{print $4}' | sort -r -V))
macOSSoftwareUpdateGBS=($(echo "$softwareUpdateLIST" | grep 'Title: macOS' | awk -F ': ' '{print $4}' | grep -o -E '[0-9]+' | awk '{print $1"/1000000 +1"}' | bc))
macOSSoftwareUpdateLABEL=$(echo "${macOSSoftwareUpdateLABELS[*]}" | grep "$macOSMAJOR.\d")
macOSSoftwareUpdateTITLE=$(echo "${macOSSoftwareUpdateTITLES[*]}" | grep "$macOSMAJOR.\d")
macOSSoftwareUpdateVERSION=$(echo "${macOSSoftwareUpdateVERSIONS[*]}" | grep "$macOSMAJOR.\d")
macOSSoftwareUpdateGB=$(echo "$softwareUpdateLIST" | grep 'Title: macOS' | grep " $macOSMAJOR.\d" | awk -F ': ' '{print $4}' | grep -o -E '[0-9]+' | awk '{print $1"/1000000 +1"}' | bc)
elif [[ $macOSMAJOR -ge 11 ]] || [[ $macOSVERSION -ge 1015 ]]; then
allSoftwareUpdateLABELS=($(echo "$softwareUpdateLIST" | awk -F ': ' '/Label:/{print $2}'))
allSoftwareUpdateTITLES=($(echo "$softwareUpdateLIST" | awk -F ',' '/Title:/ {print $1}' | cut -d ' ' -f 2-))
macOSSoftwareUpdateLABEL=$(echo "$softwareUpdateLIST" | grep 'Label: macOS' | sed -e 's/* Label: //')
macOSSoftwareUpdateTITLE=$(echo "$softwareUpdateLIST" | grep 'Title: macOS' | sed -e 's/,/:/g' | awk -F ': ' '{print $2}')
macOSSoftwareUpdateGB=$(echo "$softwareUpdateLIST" | grep 'Title: macOS' | awk -F ': ' '{print $4}' | grep -o -E '[0-9]+' | awk '{print $1"/1000000 +1"}' | bc)
if [[ $macOSMAJOR -eq 11 ]]; then
macOSSoftwareUpdateVERSION=$(echo "$macOSSoftwareUpdateLABEL" | grep -o '11.[0-9].[0-9]')
else
macOSSoftwareUpdateVERSION=$(echo "$macOSSoftwareUpdateLABEL" | grep -o '10.[0-9][0-9].[0-9]')
fi
else # macOS 10.14 or older
allSoftwareUpdateLABELS=($(echo "$softwareUpdateLIST" | awk -F '*' '/\*/{print $2}' | sed 's/^ //'))
allSoftwareUpdateTITLES=($(echo "$softwareUpdateLIST" | awk -F '(' '/\t/ {print $1}' | cut -d $'\t' -f 2 | sed 's/.$//'))
macOSSoftwareUpdateLABEL=$(echo "$softwareUpdateLIST" | grep 'macOS' | awk -F '*' '/\*/{print $2}' | sed 's/^ //')
macOSSoftwareUpdateTITLE=$(echo "$softwareUpdateLIST" | grep 'macOS' | awk -F '(' '/\t/ {print $1}' | cut -d $'\t' -f 2 | sed 's/.$//')
macOSSoftwareUpdateGB=$(echo "$softwareUpdateLIST" | grep 'macOS' | awk -F ',' '{print $2}' | grep -o -E '[0-9]+' | awk '{print $1"/1000000 +1"}' | bc)
macOSSoftwareUpdateVERSION=$(echo "$macOSSoftwareUpdateLABEL" | grep -o '10.[0-9][0-9].[0-9]')
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: allSoftwareUpdateLABELS[] is:\n${allSoftwareUpdateLABELS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: allSoftwareUpdateTITLES[] is:\n${allSoftwareUpdateTITLES[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateLABELS[] is:\n${macOSSoftwareUpdateLABELS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateTITLES[] is:\n${macOSSoftwareUpdateTITLES[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSIONS[] is:\n${macOSSoftwareUpdateVERSIONS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateGBS[] is:\n${macOSSoftwareUpdateGBS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateLABEL is: $macOSSoftwareUpdateLABEL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateTITLE is: $macOSSoftwareUpdateTITLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSION is: $macOSSoftwareUpdateVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateGB is: $macOSSoftwareUpdateGB"
[[ "${allSoftwareUpdateLABELS[*]}" != "$macOSSoftwareUpdateLABEL" ]] && recommendedSoftwareUpdateLABELS=($(echo "${allSoftwareUpdateLABELS[*]}" | grep -v 'macOS'))
[[ "${allSoftwareUpdateTITLES[*]}" != "$macOSSoftwareUpdateTITLE" ]] && recommendedSoftwareUpdateTITLES=($(echo "${allSoftwareUpdateTITLES[*]}" | grep -v 'macOS'))
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: recommendedSoftwareUpdateLABELS[] is:\n${recommendedSoftwareUpdateLABELS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: recommendedSoftwareUpdateTITLES[] is:\n${recommendedSoftwareUpdateTITLES[*]}"
# If software updates were found then output to log and set $softwareUpdateMACOS and $softwareUpdateRECOMMENDED accordingly.
if [[ -n ${allSoftwareUpdateLABELS[*]} ]]; then
if [[ -n $macOSSoftwareUpdateLABEL ]]; then
if [[ $macOSMAJOR -ge 12 ]] && [[ ${#macOSSoftwareUpdateLABELS[@]} -gt 1 ]]; then
sendToLog "Status: ${#macOSSoftwareUpdateLABELS[@]} macOS versions available via softwareupdate."
for i in "${!macOSSoftwareUpdateLABELS[@]}"; do
sendToLog "Status: macOS update $((i + 1)): ${macOSSoftwareUpdateLABELS[i]}"
done
fi
if [[ $macOSMAJOR -ge 13 ]]; then
[[ $(echo "$macOSSoftwareUpdateVERSION" | grep -c '[a-z]') -gt 0 ]] && softwareUpdateRSR="TRUE"
if [[ "$softwareUpdateRSR" == "TRUE" ]]; then
if [[ "$allowRSRUpdatesOPTION" == "TRUE" ]]; then
sendToLog "Status: macOS Rapid Security Response update available: $macOSSoftwareUpdateLABEL."
softwareUpdateMACOS="TRUE"
else # RSR updates should are not allowed.
sendToLog "Status: macOS Rapid Security Response update available but not allowed: $macOSSoftwareUpdateLABEL."
fi
else
sendToLog "Status: macOS update available: $macOSSoftwareUpdateLABEL"
softwareUpdateMACOS="TRUE"
fi
else
sendToLog "Status: macOS update available: $macOSSoftwareUpdateLABEL"
softwareUpdateMACOS="TRUE"
fi
else
sendToLog "Status: No available macOS update(s) or they may be deferred via configuration profile."
fi
if [[ -n ${recommendedSoftwareUpdateLABELS[*]} ]]; then
sendToLog "Status: ${#recommendedSoftwareUpdateLABELS[@]} available recommended (non-system) update(s)."
for i in "${!recommendedSoftwareUpdateLABELS[@]}"; do
sendToLog "Status: Recommended (non-system) update $((i + 1)): ${recommendedSoftwareUpdateLABELS[i]}"
done
softwareUpdateRECOMMENDED="TRUE"
else
sendToLog "Status: No available recommended (non-system) update(s) or they may be deferred via configuration profile."
fi
else
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to parse softwareupdate results, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to parse softwareupdate results, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to parse softwareupdate results, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to parse softwareupdate results, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
IFS="$oldIFS"
else # No available software updates.
sendToLog "Status: No available software update(s) or they may be deferred via configuration profile."
defaults delete "$superPLIST" SoftwareUpdatesList 2> /dev/null
defaults write "$superPLIST" SoftwareUpdatesList -bool false
defaults delete "$superPLIST" macOSSoftwareUpgradeLabel 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpgradeTitle 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeName 2> /dev/null
defaults delete "$superPLIST" macOSUpgradeVersion 2> /dev/null
defaults delete "$superPLIST" macOSSoftwareUpdateDownloadLabel 2> /dev/null
defaults delete "$superPLIST" LastReboot 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateRSR is: $softwareUpdateRSR"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateRECOMMENDED is: $softwareUpdateRECOMMENDED"
}
# This function checks for available macOS upgrades for systems older than macOS 12.3 and sets $macOSUpgradeAVAILABLE accordingly. This is in a separate function to facilitate list caching and multiple run workflows.
# Also sets $macOSUpgradeNAME, $macOSUpgradeVERSION, $macOSUpgradeMajorVERSION, and $macOSInstallerLIST.
listMacOSUpgrades() {
macOSUpgradeAVAILABLE="FALSE"
# Use built-in commands to determine if a macOS upgrade is available.
if [[ $macOSVERSION -ge 1015 ]]; then # For macOS 10.15 or newer parse results from the mdmclient commmand.
sendToLog "mdmclient: Waiting for macOS upgrade list..."
availableOSUPDATES=$(/usr/libexec/mdmclient AvailableOSUpdates 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: availableOSUPDATES is:\n$availableOSUPDATES"
macOSUPGRADE=$(echo "$availableOSUPDATES" | grep -e 'HumanReadableName =' -e 'Version =' | tr '\n' '-' | sed -e 's/ HumanReadableName = //g' -e 's/;- Version = //g' | tr ';-' '\n' | grep -e 'macOS' | sed -e 's/""/:/g' -e 's/"//g' | sort -t: -k3 -r -V | head -1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUPGRADE is: $macOSUPGRADE"
macOSUpgradeNAME=$(echo "$macOSUPGRADE"| awk -F ':' '{print $1}' | sed -e 's/[0-9]*//g' -e 's/ \.//')
macOSUpgradeVERSION=$(echo "$macOSUPGRADE"| awk -F ':' '{print $2}')
macOSUpgradeMajorVERSION=$(echo "$macOSUpgradeVERSION" | cut -d '.' -f1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeNAME is: $macOSUpgradeNAME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVERSION is: $macOSUpgradeVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeMajorVERSION is: $macOSUpgradeMajorVERSION"
{ [[ -n $macOSUpgradeNAME ]] && [[ -n $macOSUpgradeVERSION ]] && [[ $macOSUpgradeMajorVERSION -gt $macOSMAJOR ]]; } && macOSUpgradeAVAILABLE="TRUE"
else # Older versions of macOS collect this from $asuPLIST.
propertyUpgradeAVAILABLE=$(defaults read "$asuPLIST" LastRecommendedMajorOSBundleIdentifier 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: propertyUpgradeAVAILABLE is: $propertyUpgradeAVAILABLE"
macOSUpgradeNAME=$(echo "$propertyUpgradeAVAILABLE" | sed -e 's/com.apple.InstallAssistant.//' -e 's/macOS/macOS /')
macOSUpgradeVERSION="" # Not necessary (or easy) to collect for systems this old.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeNAME is: $macOSUpgradeNAME"
[[ -n $macOSUpgradeNAME ]] && macOSUpgradeAVAILABLE="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeAVAILABLE is: $macOSUpgradeAVAILABLE"
# Only collect available macOS installer names if a $upgradeWORKFLOW is requested.
if [[ "$macOSUpgradeAVAILABLE" == "TRUE" ]] && [[ "$upgradeWORKFLOW" != "FALSE" ]]; then
# Background the erase-install list process and send to $installerListLOG.
if [[ "$betaWORKFLOW" != "FALSE" ]]; then
"$eraseInstallSCRIPT" --list --seed "$betaWORKFLOW" > "$installerListLOG" 2>&1 &
eraseInstallPID=$!
else # Standard non-beta workflow.
"$eraseInstallSCRIPT" --list > "$installerListLOG" 2>&1 &
eraseInstallPID=$!
fi
# Watch $installerListLOG while waiting for the erase-install.sh list process to complete. Note this while read loop has a timeout based on $initialStartTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'Cannot continue.' ) -gt 0 ]] || [[ $(echo "$macOSInstallerLIST" | grep -c 'exit code: 1') -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'script execution started') -gt 0 ]]; then
if [[ "$betaWORKFLOW" != "FALSE" ]]; then
sendToLog "erase-install.sh: Waiting for macOS installer beta $betaWORKFLOW list..."
else # Standard non-beta workflow.
sendToLog "erase-install.sh: Waiting for macOS installer list..."
fi
elif [[ $(echo "$logLINE" | grep -c 'finish') -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
break
fi
done < <(tail -n1 -F "$installerListLOG")
# If the erase-install.sh list completed, then collect information.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]]; then
macOSInstallerLIST=$(<"$installerListLOG")
elif [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToLog "Error: erase-install.sh list timed out after $workflowTimeoutSECONDS seconds."
kill -9 "$eraseInstallPID" > /dev/null 2>&1
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToLog "Error: erase-install.sh list failed, check $installerListLOG for more detail."
kill -9 "$eraseInstallPID" > /dev/null 2>&1
fi
fi
}
# This function evaluates macOS upgrade information collected from the checkMacOSUpgrades() or listMacOSUpgrades() functions and sets super macOS upgrade caches and $macOSUpgradeVersionTARGET accordingly.
# For systems with macOS 12.3 or newer this also sets $macOSSoftwareUpgradeLabelTARGET, $macOSSoftwareUpgradeTitleTARGET, and $macOSSoftwareUpgradeVersionTARGET.
# For systems older than macOS 12.3 this also sets $macOSInstallerNameTARGET, $macOSInstallerVersionTARGET, and $macOSInstallerBuildTARGET.
checkMacOSUpgrades() {
macOSUpgradeVersionTARGET="FALSE"
unset macOSSoftwareUpgradeLABEL
unset macOSSoftwareUpgradeTITLE
unset macOSSoftwareUpgradeVERSION
unset macOSSoftwareUpgradeGB
unset macOSInstallerNAMES
unset macOSInstallerVERSIONS
unset macOSInstallerBUILDS
unset macOSSoftwareUpgradeLabelTARGET
unset macOSSoftwareUpgradeTitleTARGET
unset macOSSoftwareUpgradeVersionTARGET
unset macOSInstallerNameTARGET
unset macOSInstallerVersionTARGET
unset macOSInstallerBuildTARGET
# Only need to list macOS upgrades if a full check is required.
if [[ "$fullCheckREQUIRED" == "TRUE" ]]; then
sendToLog "Status: Starting full check for macOS upgrades..."
listMacOSUpgrades
# Double-checking in case macOS installer list workflows are misbehaving.
if [[ "$workflowStartFAIL" == "TRUE" ]] && [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToLog "Status: Re-checking for macOS upgrades..."
listMacOSUpgrades
fi
# macOS installer list check failed, try again later.
if [[ "$workflowStartFAIL" == "TRUE" ]] && [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Checking for macOS installers did not complete after multiple attempts, install now workflow can not continue."
sendToStatus "Inactive Error: Checking for macOS installers did not complete after multiple attempts, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Checking for macOS installers did not complete after multiple attempts, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Checking for macOS installers did not complete after multiple attempts, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
oldIFS="$IFS"; IFS=$'\n'
# For macOS 12.3 or newer parse the results from $macOSSoftwareUpdateVERSIONS[].
if [[ $macOSVERSION -ge 1203 ]] && [[ "$softwareUpdatesAVAILABLE" == "TRUE" ]]; then
macOSUpgradeAVAILABLE="FALSE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSIONS[] is:\n${macOSSoftwareUpdateVERSIONS[*]}"
if [[ $(echo "${macOSSoftwareUpdateVERSIONS[0]}" | cut -d '.' -f1) -gt $macOSMAJOR ]]; then
macOSSoftwareUpgradeLABEL="${macOSSoftwareUpdateLABELS[0]}"
macOSSoftwareUpgradeTITLE="${macOSSoftwareUpdateTITLES[0]}"
macOSSoftwareUpgradeVERSION="${macOSSoftwareUpdateVERSIONS[0]}"
macOSSoftwareUpgradeGB="${macOSSoftwareUpdateGBS[0]}"
macOSUpgradeAVAILABLE="TRUE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeLABEL is: $macOSSoftwareUpgradeLABEL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeTITLE is: $macOSSoftwareUpgradeTITLE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeVERSION is: $macOSSoftwareUpgradeVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeGB is: $macOSSoftwareUpgradeGB"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeAVAILABLE is: $macOSUpgradeAVAILABLE"
fi
# If macOS upgrades are available then validate and save the appropriate information.
if [[ "$macOSUpgradeAVAILABLE" == "TRUE" ]]; then
# Save macOS upgrade information for cache.
if [[ $macOSVERSION -ge 1203 ]]; then # Newer systems upgrade via softwareupdate.
defaults write "$superPLIST" macOSSoftwareUpgradeLabel -string "$macOSSoftwareUpgradeLABEL"
defaults write "$superPLIST" macOSSoftwareUpgradeTitle -string "$macOSSoftwareUpgradeTITLE"
fi
defaults write "$superPLIST" macOSUpgradeName -string "$macOSUpgradeNAME"
[[ $macOSVERSION -ge 1015 ]] && defaults write "$superPLIST" macOSUpgradeVersion -string "$macOSUpgradeVERSION"
# If macOS upgrades are allowed then set various macOS upgrade targets.
if [[ "$upgradeWORKFLOW" != "FALSE" ]]; then
# Parse results from $macOSInstallerLIST.
if [[ "$macOSUpgradeAVAILABLE" == "TRUE" ]]; then
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerLIST is:\n$macOSInstallerLIST"
macOSInstallerCompatibleLIST=($(echo "$macOSInstallerLIST" | grep 'macOS' | grep -v -e 'Unsupported' -e 'macOS installers' -e 'check_installer_is_valid' | sed -E -e 's/ +/:/g' -e 's/ 20/:20/g'))
macOSInstallersARRAY=($(sort -t: -k4 -r <<<"${macOSInstallerCompatibleLIST[*]}"))
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallersARRAY[] is:\n${macOSInstallersARRAY[*]}"
if [[ -n ${macOSInstallersARRAY[*]} ]]; then
for macOSInstallerLINE in "${macOSInstallersARRAY[@]}"; do
macOSInstallerNAMES+=($(echo "$macOSInstallerLINE" | awk -F ':' '{print $6;}'))
macOSInstallerVERSIONS+=($(echo "$macOSInstallerLINE" | awk -F ':' '{print $3;}'))
macOSInstallerBUILDS+=($(echo "$macOSInstallerLINE" | awk -F ':' '{print $4;}'))
done
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerNAMES[] is:\n${macOSInstallerNAMES[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerVERSIONS[] is:\n${macOSInstallerVERSIONS[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerBUILDS[] is:\n${macOSInstallerBUILDS[*]}"
else
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to find compatible macOS installers, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to find compatible macOS installers, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to find compatible macOS installers, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to find compatible macOS installers, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
# Evaluate conditions necessary to set $macOSUpgradeVersionTARGET.
if [[ -n $targetUpgradeOPTION ]]; then # A specific macOS upgrade version has been specified.
if [[ $targetUpgradeOPTION -lt $macOSMAJOR ]] || [[ $targetUpgradeOPTION -eq $macOSMAJOR ]]; then
[[ $targetUpgradeOPTION -lt $macOSMAJOR ]] && sendToLog "Warning: Target upgrade version of macOS $targetUpgradeOPTION is less than currently installed macOS $macOSMAJOR."
[[ $targetUpgradeOPTION -eq $macOSMAJOR ]] && sendToLog "Status: Target upgrade version of macOS $targetUpgradeOPTION is the same as currently installed macOS $macOSMAJOR."
macOSUpgradeVersionTARGET="FALSE"
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then
sendToLog "Status: macOS upgrade available via softwareupdate but not allowed: $macOSSoftwareUpgradeTITLE"
else
sendToLog "Status: macOS upgrade available via installer but not allowed: $macOSUpgradeNAME $macOSUpgradeVERSION"
fi
else # macOS upgrade target is greater than current macOS version, so this is the only time it would matter.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then # Newer systems upgrade via softwareupdate unless via MDM workflow.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeVERSION is: $macOSSoftwareUpgradeVERSION"
if [[ $targetUpgradeOPTION -gt $(echo "$macOSSoftwareUpgradeVERSION" | cut -d '.' -f1) ]]; then # macOS upgrade target is greater than the newest available macOS software upgrade.
macOSSoftwareUpgradeLabelTARGET="$macOSSoftwareUpgradeLABEL"
macOSSoftwareUpgradeTitleTARGET="$macOSSoftwareUpgradeTITLE"
macOSSoftwareUpgradeVersionTARGET="$macOSSoftwareUpgradeVERSION"
sendToLog "Warning: Requested target macOS upgrade version of $targetUpgradeOPTION is greater than the currently available macOS upgrade version of $macOSSoftwareUpgradeVersionTARGET."
sendToLog "Status: macOS upgrade available via softwareupdate: $macOSSoftwareUpgradeLabelTARGET"
else # macOS upgrade target is lower than the newest available macOS software upgrade.
for i in "${!macOSSoftwareUpdateVERSIONS[@]}"; do
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSIONS[i] is: ${macOSSoftwareUpdateVERSIONS[i]}"
if [[ "$(echo "${macOSSoftwareUpdateVERSIONS[i]}" | cut -d '.' -f1)" == "$targetUpgradeOPTION" ]]; then
macOSSoftwareUpgradeLabelTARGET="${macOSSoftwareUpdateLABELS[i]}"
macOSSoftwareUpgradeTitleTARGET="${macOSSoftwareUpdateTITLES[i]}"
macOSSoftwareUpgradeVersionTARGET="${macOSSoftwareUpdateVERSIONS[i]}"
break
fi
done
sendToLog "Status: Targeting specific macOS upgrade available via softwareupdate: macOSSoftwareUpgradeLabelTARGET"
fi
macOSUpgradeVersionTARGET="$macOSSoftwareUpgradeVersionTARGET"
else # Systems older than macOS 12.3 or using MDM workflow upgrade via installer.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerVERSIONS[0] is: ${macOSInstallerVERSIONS[0]}"
if [[ $targetUpgradeOPTION -gt $(echo "${macOSInstallerVERSIONS[0]}" | cut -d '.' -f1) ]]; then # macOS upgrade target is greater than the newest available macOS installer.
macOSInstallerNameTARGET="${macOSInstallerNAMES[0]}"
macOSInstallerVersionTARGET="${macOSInstallerVERSIONS[0]}"
macOSInstallerBuildTARGET="${macOSInstallerBUILDS[0]}"
sendToLog "Warning: Requested target macOS upgrade version of $targetUpgradeOPTION is greater than the currently available macOS upgrade version of $macOSInstallerVersionTARGET."
sendToLog "Status: macOS upgrade available via installer: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET"
else # macOS upgrade target is lower than the newest available macOS installer.
for macOSInstallerLINE in "${macOSInstallersARRAY[@]}"; do
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerLINE is: $macOSInstallerLINE"
if [[ "$(echo "$macOSInstallerLINE" | awk -F ':' '{print $3;}' | cut -d '.' -f1)" == "$targetUpgradeOPTION" ]]; then
macOSInstallerNameTARGET="$(echo "$macOSInstallerLINE" | awk -F ':' '{print $6;}')"
macOSInstallerVersionTARGET="$(echo "$macOSInstallerLINE" | awk -F ':' '{print $3;}')"
macOSInstallerBuildTARGET="$(echo "$macOSInstallerLINE" | awk -F ':' '{print $4;}')"
break
fi
done
sendToLog "Status: Targeting specific macOS upgrade available via installer: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET"
fi
macOSUpgradeVersionTARGET="$macOSInstallerVersionTARGET"
fi
fi
else # There is no specified target version, so assume available upgrade is the target.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then # Newer systems upgrade via softwareupdate unless via MDM workflow.
macOSSoftwareUpgradeLabelTARGET="$macOSSoftwareUpgradeLABEL"
macOSSoftwareUpgradeTitleTARGET="$macOSSoftwareUpgradeTITLE"
sendToLog "Status: macOS upgrade available via softwareupdate: $macOSSoftwareUpgradeLabelTARGET"
macOSSoftwareUpgradeVersionTARGET=$(echo "$macOSSoftwareUpgradeLabelTARGET" | awk -F '-' '{print $1}' | sed -e 's/[a-zA-Z ]*//g')
macOSUpgradeVersionTARGET="$macOSSoftwareUpgradeVersionTARGET"
else # Systems older than macOS 12.3 or using MDM workflow upgrade via installer.
macOSInstallerNameTARGET="${macOSInstallerNAMES[0]}"
macOSInstallerVersionTARGET="${macOSInstallerVERSIONS[0]}"
macOSInstallerBuildTARGET="${macOSInstallerBUILDS[0]}"
sendToLog "Status: macOS upgrade available via installer: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET"
macOSUpgradeVersionTARGET="$macOSInstallerVersionTARGET"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeLabelTARGET is: $macOSSoftwareUpgradeLabelTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeTitleTARGET is: $macOSSoftwareUpgradeTitleTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpgradeVersionTARGET is: $macOSSoftwareUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerNameTARGET is: $macOSInstallerNameTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerVersionTARGET is: $macOSInstallerVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerBuildTARGET is: $macOSInstallerBuildTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
else # macOS upgrade is not allowed.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then
sendToLog "Status: macOS upgrade available via softwareupdate but not allowed: $macOSSoftwareUpgradeTITLE"
else
sendToLog "Status: macOS upgrade available via installer but not allowed: $macOSUpgradeNAME $macOSUpgradeVERSION"
fi
fi
else # No available macOS upgrade.
sendToLog "Status: No available macOS upgrade or it may be deferred via configuration profile."
[[ $macOSVERSION -ge 1203 ]] && defaults write "$superPLIST" macOSSoftwareUpgradeLabel -bool false
[[ $macOSVERSION -ge 1203 ]] && defaults write "$superPLIST" macOSSoftwareUpgradeTitle -bool false
defaults write "$superPLIST" macOSUpgradeName -bool false
[[ $macOSVERSION -ge 1015 ]] && defaults write "$superPLIST" macOSUpgradeVersion -bool false
fi
IFS="$oldIFS"
}
# Delete any unneeded macOS installers based on the value of $macOSUpgradeVersionTARGET in order to save space.
deleteUnneededMacOSInstallers() {
oldIFS="$IFS"; IFS=$'\n'
foundMacOSInstallersARRAY=($(mdfind kind:app -name "Install macOS" 2> /dev/null))
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: foundMacOSInstallersARRAY[] is:\n${foundMacOSInstallersARRAY[*]}"
if [[ -n ${foundMacOSInstallersARRAY[*]} ]]; then
for installerPATH in "${foundMacOSInstallersARRAY[@]}"; do
if [[ $(echo "$installerPATH" | grep -c '/Users/') -gt 0 ]] && [[ $(echo "$installerPATH" | grep -c '/Users/.*/Applications/') -eq 0 ]] && [[ $(echo "$installerPATH" | grep -c '/Users/.*/Desktop/') -eq 0 ]] && [[ $(echo "$installerPATH" | grep -c '/Users/.*/Downloads/') -eq 0 ]]; then
sendToLog "Status: Skipping deletion of assumed archived macOS installer at: $installerPATH"
else
if [[ "$macOSUpgradeVersionTARGET" == "FALSE" ]]; then
if [[ "$testModeOPTION" != "TRUE" ]]; then
sendToLog "Warning: macOS upgrades are not allowed, removing unnecessary macOS installer at: $installerPATH"
rm -Rf "$installerPATH" > /dev/null 2>&1
else # Test mode.
sendToLog "Test Mode: macOS upgrades are not allowed, found unnecessary macOS installer at: $installerPATH"
fi
elif [[ "$installerPATH" != "/Applications/Install $macOSInstallerNameTARGET.app" ]]; then
if [[ "$testModeOPTION" != "TRUE" ]]; then
sendToLog "Warning: Removing unnecessary macOS installer at: $installerPATH"
rm -Rf "$installerPATH" > /dev/null 2>&1
else # Test mode.
sendToLog "Test Mode: Found unnecessary macOS installer at: $installerPATH"
fi
fi
fi
done
fi
IFS="$oldIFS"
}
# This function determines which macOS updates, upgrades, or installers should be downloaded and validates any previously downloaded macOS updates, upgrades, or installers and sets $macOSInstallerDownloadREQUIRED, $macOSSoftwareUpdateDownloadREQUIRED, $softwareUpdateLabelTARGET, and $softwareUpdateTitleTARGET accordingly.
checkMacOSDownloads() {
macOSInstallerDownloadREQUIRED="FALSE"
macOSSoftwareUpdateDownloadREQUIRED="FALSE"
softwareUpdateLabelTARGET=""
softwareUpdateTitleTARGET=""
# If needed, evaluate previously downloaded macOS installer and set $macOSInstallerDownloadREQUIRED accordingly.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSVERSION is: $macOSVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: upgradeWORKFLOW is: $upgradeWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeAVAILABLE is: $macOSUpgradeAVAILABLE"
if { [[ $macOSVERSION -lt 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; } || { [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]] && [[ "$upgradeWORKFLOW" == "JAMF" ]]; }; then
macOSInstallerDownloadVERSION=$(defaults read "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null)
macOSInstallerDownloadNAME=$(defaults read "$superPLIST" macOSInstallerDownloadName 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerDownloadVERSION is: $macOSInstallerDownloadVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerDownloadNAME is: $macOSInstallerDownloadNAME"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerVersionTARGET is: $macOSInstallerVersionTARGET"
if [[ -n $macOSInstallerDownloadVERSION ]] && [[ -n $macOSInstallerDownloadNAME ]] && [[ "$fullCheckREQUIRED" == "FALSE" ]]; then
if [[ "$macOSInstallerDownloadVERSION" == "$macOSInstallerVersionTARGET" ]]; then
if [[ ! -d "/Applications/Install $macOSInstallerNameTARGET.app" ]]; then
sendToLog "Status: Previously downloaded macOS installer could not be found."
macOSInstallerDownloadREQUIRED="TRUE"
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
restartZeroDay
fi
else
sendToLog "Status: Previously downloaded installer for macOS $macOSInstallerDownloadVERSION does not match current target upgrade of macOS macOSInstallerVersionTARGET."
macOSInstallerDownloadREQUIRED="TRUE"
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
restartZeroDay
fi
else # If there is no cached $macOSInstallerDownloadVERSION, $macOSInstallerDownloadNAME, or a full check is required.
macOSInstallerDownloadREQUIRED="TRUE"
fi
elif [[ "$softwareUpdateMACOS" == "TRUE" ]] || [[ "$macOSUpgradeAVAILABLE" == "TRUE" ]]; then # Evaluate previously downloaded macOS updates or upgrades and compare them to currently available, setting $macOSSoftwareUpdateDownloadREQUIRED accordingly.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # macOS 12.3 or newer can upgrade via softwareupdate.
softwareUpdateLabelTARGET="$macOSSoftwareUpgradeLabelTARGET"
softwareUpdateTitleTARGET="$macOSSoftwareUpgradeTitleTARGET"
else # Standard macOS update.
softwareUpdateLabelTARGET="$macOSSoftwareUpdateLABEL"
softwareUpdateTitleTARGET="$macOSSoftwareUpdateTITLE"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateLabelTARGET is: $softwareUpdateLabelTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateTitleTARGET is: $softwareUpdateTitleTARGET"
macOSSoftwareUpdateDownloadLABEL=$(defaults read "$superPLIST" macOSSoftwareUpdateDownloadLabel 2> /dev/null)
previousLastREBOOT="$(defaults read "$superPLIST" LastReboot 2> /dev/null)"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateDownloadLABEL is: $macOSSoftwareUpdateDownloadLABEL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: previousLastREBOOT is: $previousLastREBOOT"
# Only validate if we know the list of previous downloads and last reboot time.
if [[ -n $macOSSoftwareUpdateDownloadLABEL ]] && [[ -n $previousLastREBOOT ]]; then
previousDownloadFAILURE="FALSE"
if [[ "$macOSSoftwareUpdateDownloadLABEL" != "$softwareUpdateLabelTARGET" ]]; then
sendToLog "Warning: Previously downloaded update/upgrade \"$macOSSoftwareUpdateDownloadLABEL\" does not match the expected update/upgrade \"$softwareUpdateLabelTARGET\", download workflow needs to run again."
previousDownloadFAILURE="TRUE"
restartZeroDay
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: lastREBOOT is: $lastREBOOT"
if [[ "$lastREBOOT" != "$previousLastREBOOT" ]]; then
sendToLog "Warning: The system has been restarted without applying the previously downloaded macOS update, download workflow needs to run again."
previousDownloadFAILURE="TRUE"
fi
# Only validate prepared macOS update/upgrade if needed.
if { [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; } || { [[ $macOSMAJOR -ge 11 ]] && [[ "$softwareUpdateMACOS" == "TRUE" ]]; }; then
preparedMacOSATTRIBUTES=$(defaults read /System/Volumes/Update/Update update-asset-attributes 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSATTRIBUTES is:\n$preparedMacOSATTRIBUTES"
preparedMacOSVERSION=$(echo "$preparedMacOSATTRIBUTES" | grep -w 'OSVersion' | awk -F '"' '{print $2}')
if [[ $macOSMAJOR -ge 13 ]]; then
preparedMacOSExtraVERSION=$(echo "$preparedMacOSATTRIBUTES" | grep -w 'ProductVersionExtra' | awk -F '"' '{print $2}')
[[ -n $preparedMacOSExtraVERSION ]] && preparedMacOSVERSION="$preparedMacOSVERSION $preparedMacOSExtraVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSExtraVERSION is: $preparedMacOSExtraVERSION"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSVERSION is: $preparedMacOSVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSION is: $macOSSoftwareUpdateVERSION"
if [[ -z $preparedMacOSVERSION ]]; then
sendToLog "Warning: Previously downloaded macOS update/upgrade is no longer valid, download workflow needs to run again."
previousDownloadFAILURE="TRUE"
else
if { [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; } && [[ "$preparedMacOSVERSION" != "$macOSUpgradeVersionTARGET" ]]; then
sendToLog "Warning: Previously downloaded macOS upgrade version $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, download workflow needs to run again."
previousDownloadFAILURE="TRUE"
fi
if { [[ $macOSMAJOR -ge 11 ]] && [[ "$macOSUpgradeVersionTARGET" == "FALSE" ]]; } && [[ "$preparedMacOSVERSION" != "$macOSSoftwareUpdateVERSION" ]]; then
sendToLog "Warning: Previously downloaded macOS update version $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, download workflow needs to run again."
previousDownloadFAILURE="TRUE"
fi
fi
if [[ "$previousDownloadFAILURE" == "TRUE" ]]; then
macOSSoftwareUpdateDownloadREQUIRED="TRUE"
defaults delete "$superPLIST" macOSSoftwareUpdateDownloadLabel 2> /dev/null
defaults delete "$superPLIST" LastReboot 2> /dev/null
fi
fi
else
macOSSoftwareUpdateDownloadREQUIRED="TRUE"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSInstallerDownloadREQUIRED is: $macOSInstallerDownloadREQUIRED"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateDownloadREQUIRED is: $macOSSoftwareUpdateDownloadREQUIRED"
}
# This function checks the macOS upgrade/update status after a previous super macOS upgrade/update restart.
checkAfterRestart() {
unset restartVALIDATE
fullCheckREQUIRED="TRUE"
checkSoftwareUpdates
checkMacOSUpgrades
checkMacOSDownloads
# Install any recommended (non-system) software updates.
if [[ "$softwareUpdateRECOMMENDED" == "TRUE" ]]; then
installRecommendedSoftwareUpdates
if [[ "$softwareUpdateERROR" != "TRUE" ]]; then
sendToLog "Status: Completed installation of all recommended (non-system) updates."
else
sendToLog "Warning: Failed to install all recommended (non-system) updates."
fi
fullCheckREQUIRED="TRUE"
checkSoftwareUpdates
checkMacOSUpgrades
checkMacOSDownloads
fi
# Log status of updates/upgrade completion.
if [[ "$softwareUpdateRECOMMENDED" == "FALSE" ]] && [[ "$softwareUpdateMACOS" == "FALSE" ]] && { [[ "$macOSUpgradeAVAILABLE" == "FALSE" ]] || { [[ "$macOSUpgradeAVAILABLE" != "FALSE" ]] && [[ "$upgradeWORKFLOW" == "FALSE" ]]; }; }; then
sendToLog "Status: All available and enabled software updates/upgrade completed."
fullCheckREQUIRED="FALSE"
else
sendToLog "Warning: Some software updates/upgrade did not complete after last restart, continuing workflow."
fi
# For computers managed via Jamf Pro, submit inventory and check for policies.
if [[ "$jamfVERSION" != "FALSE" ]]; then
if [[ "$jamfSERVER" != "FALSE" ]]; then
sendToLog "Status: Submitting updated inventory to Jamf Pro. Use --verbose-mode or check /var/log/jamf.log for more detail..."
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
jamfRESULT=$("$jamfBINARY" recon -verbose 2>&1)
jamfRETURN=$?
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRESULT is:\n$jamfRESULT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRETURN is: $jamfRETURN"
else
"$jamfBINARY" recon > /dev/null 2>&1
fi
sleep 5
sendToLog "Status: Running Jamf Pro check-in policies. Use --verbose-mode or check /var/log/jamf.log for more detail..."
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
jamfRESULT=$("$jamfBINARY" policy -verbose 2>&1)
jamfRETURN=$?
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRESULT is:\n$jamfRESULT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRETURN is: $jamfRETURN"
else
"$jamfBINARY" policy > /dev/null 2>&1
fi
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to submit inventory to Jamf Pro, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to submit inventory to Jamf Pro, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
}
# MARK: *** Pre-Installation Prep ***
################################################################################
# Download macOS update or upgrade via softwareupdate command, and also save results to $superLOG, $asuLOG, and $superPLIST.
downloadMacOSSoftwareUpdate() {
sendToLog "softwareupdate: Starting $softwareUpdateTitleTARGET download workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting $softwareUpdateTitleTARGET download workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD $softwareUpdateTitleTARGET SOFTWAREUPDATE START ****"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateLabelTARGET[] is:\n${softwareUpdateLabelTARGET[*]}"
# The softwareupdate download process is backgrounded and is watched via a while loop later on. Also note the difference between macOS versions.
if [[ $macOSMAJOR -ge 13 ]]; then # macOS 13+
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
echo ' ' | launchctl asuser "$currentUserUID" sudo -u "$currentUserNAME" softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license --user "$currentUserNAME" --stdinpass >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
launchctl asuser "$currentUserUID" sudo -u "$currentUserNAME" softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
elif [[ $macOSMAJOR -ge 12 ]]; then # macOS 12
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
launchctl asuser "$currentUserUID" sudo -u root softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license --user "root" --stdinpass "" >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
launchctl asuser "$currentUserUID" sudo -u root softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
elif [[ $macOSMAJOR -eq 11 ]]; then # macOS 11
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
echo ' ' | softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license >> "$asuLOG" 2>&1 &
else # Intel.
softwareupdate --download "$softwareUpdateLabelTARGET" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
else # macOS 10.x
softwareupdate --download "$softwareUpdateLabelTARGET" >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
# Watch $asuLOG while waiting for the softwareupdate download workflow to complete.
# Note this while read loop has a timeout based on $initialStartTimeoutSECONDS then changes to $softwareUpdateTimeoutSECONDS or $softwareUpdateLegacyTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
workflowPHASE="START"
workflowCompletePERCENT=0
workflowPreviousCompletePERCENT=0
softwareUpdateDownloadTITLE=""
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c "Can’t connect" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c "Couldn't communicate" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'No such update' ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'Failed to download' ) -gt 0 ]]; then
workflowStartFAIL="CONNECT"
workflowStartTIMEOUT="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'No such update' ) -gt 0 ]]; then
workflowStartFAIL="NOUPDATE"
workflowStartTIMEOUT="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Not enough free disk space' ) -gt 0 ]]; then
workflowStartFAIL="SPACE"
workflowStartTIMEOUT="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Failed to download & prepare' ) -gt 0 ]]; then
workflowStartFAIL="FAILED"
workflowStartTIMEOUT="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $macOSMAJOR -lt 11 ]] && [[ $(echo "$logLINE" | grep -c 'Software Update Tool') -gt 0 ]]; then
sendToLog "softwareupdate: $softwareUpdateLabelTARGET is downloading, but detailed progress is not available for systems older than macOS 11..."
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="DOWNLOADING"
workflowTimeoutSECONDS=$softwareUpdateLegacyTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading') -gt 0 ]] && [[ "$workflowPHASE" == "START" ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/Downloading //')
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE is downloading..."
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="DOWNLOADING"
workflowTimeoutSECONDS=$softwareUpdateTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
[[ $(echo "$softwareUpdateDownloadTITLE" | grep -c 'macOS') -gt 0 ]] && workflowPHASE="DOWNLOADING"
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading') -gt 0 ]] && [[ "$workflowPHASE" == "DOWNLOADING" ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | sed -e 's/Downloading: //' -e 's/\.[0-9][0-9]//' | tr -d '\n' | tr -d '\r')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 60 ]]; then
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE download progress: 100%\n"
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE download complete, now preparing..."
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="PREPARING"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$( (echo "$workflowCompletePERCENT * 1.69" | bc ) | cut -d '.' -f1)
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE download progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading') -gt 0 ]] && [[ "$workflowPHASE" == "PREPARING" ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | sed -e 's/Downloading: //' -e 's/\.[0-9][0-9]//' | tr -d '\n' | tr -d '\r')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 100 ]]; then
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE preparing progress: 100%\n"
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="DONE"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$(((workflowCompletePERCENT-60)*2))
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE preparing progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c 'Downloaded') -gt 0 ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/://' -e 's/Downloaded //')
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE download and preparation complete."
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
workflowTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$asuLOG" | tr -u '%' '\n')
# If the softwareupdate download workflow completed, then validate and collect information.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]] && [[ "$workflowTIMEOUT" == "FALSE" ]]; then
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateTitleTARGET is: $softwareUpdateTitleTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateDownloadTITLE is: $softwareUpdateDownloadTITLE"
if [[ "$softwareUpdateTitleTARGET" == "$softwareUpdateDownloadTITLE" ]]; then
if [[ $macOSMAJOR -ge 11 ]]; then
preparedMacOSATTRIBUTES=$(defaults read /System/Volumes/Update/Update update-asset-attributes 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSATTRIBUTES is:\n$preparedMacOSATTRIBUTES"
preparedMacOSVERSION=$(echo "$preparedMacOSATTRIBUTES" | grep -w 'OSVersion' | awk -F '"' '{print $2}')
if [[ $macOSMAJOR -ge 13 ]]; then
preparedMacOSExtraVERSION=$(echo "$preparedMacOSATTRIBUTES" | grep -w 'ProductVersionExtra' | awk -F '"' '{print $2}')
[[ -n $preparedMacOSExtraVERSION ]] && preparedMacOSVERSION="$preparedMacOSVERSION $preparedMacOSExtraVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSExtraVERSION is: $preparedMacOSExtraVERSION"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: preparedMacOSVERSION is: $preparedMacOSVERSION"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateVERSION is: $macOSSoftwareUpdateVERSION"
if { [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; } && [[ "$preparedMacOSVERSION" != "$macOSUpgradeVersionTARGET" ]]; then
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD SOFTWAREUPDATE FAILED ****"
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToASULog "Error: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, install now workflow can not continue."
sendToLog "Error: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, install now workflow can not continue."
sendToStatus "Inactive Error: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToASULog "Error: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, trying again in $deferSECONDS seconds."
sendToLog "Error: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Downloaded macOS upgrade version of $preparedMacOSVERSION doesn't match expected version $macOSUpgradeVersionTARGET, trying again in $deferSECONDS seconds."
kickSoftwareUpdateD
makeLaunchDaemonCalendar
fi
fi
if { [[ $macOSMAJOR -ge 11 ]] && [[ "$macOSUpgradeVersionTARGET" == "FALSE" ]]; } && [[ "$preparedMacOSVERSION" != "$macOSSoftwareUpdateVERSION" ]]; then
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD SOFTWAREUPDATE FAILED ****"
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToASULog "Error: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, install now workflow can not continue."
sendToLog "Error: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, install now workflow can not continue."
sendToStatus "Inactive Error: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToASULog "Error: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, trying again in $deferSECONDS seconds."
sendToLog "Error: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Downloaded macOS update version of $preparedMacOSVERSION doesn't match expected version $macOSSoftwareUpdateVERSION, trying again in $deferSECONDS seconds."
kickSoftwareUpdateD
makeLaunchDaemonCalendar
fi
fi
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD SOFTWAREUPDATE COMPLETED ****"
defaults write "$superPLIST" macOSSoftwareUpdateDownloadLabel -string "$softwareUpdateLabelTARGET"
defaults write "$superPLIST" LastReboot -string "$lastREBOOT"
macOSSoftwareUpdateDownloadREQUIRED="FALSE"
[[ "$onlyDownloadOPTION" == "TRUE" ]] && onlyDownloadCOMPLETE="TRUE"
else # The expected $softwareUpdateTitleTARGET did not match the $softwareUpdateDownloadTITLE.
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD SOFTWAREUPDATE INCOMPLETE ****"
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToASULog "Error: Download of $softwareUpdateTitleTARGET did not complete, install now workflow can not continue."
sendToLog "Error: Download of $softwareUpdateTitleTARGET did not complete, install now workflow can not continue."
sendToStatus "Inactive Error: Download of $softwareUpdateTitleTARGET did not complete, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToASULog "Error: Download of $softwareUpdateTitleTARGET did not complete, trying again in $deferSECONDS seconds."
sendToLog "Error: Download of $softwareUpdateTitleTARGET did not complete, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Download of $softwareUpdateTitleTARGET did not complete, trying again in $deferSECONDS seconds."
kickSoftwareUpdateD
makeLaunchDaemonCalendar
fi
fi
else # The softwareupdate download workflow failed so clean-up and try again later.
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD SOFTWAREUPDATE FAILED ****"
kill -9 "$softwareupdatePID" > /dev/null 2>&1
kickSoftwareUpdateD
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "CONNECT" ]]; then
sendToASULog "Error: Unable to reach Apple Software Update server, install now workflow can not continue."
sendToLog "Error: Unable to reach Apple Software Update server, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to reach Apple Software Update server, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "NOUPDATE" ]]; then
sendToASULog "Error: Unable to find requested software updates, install now workflow can not continue."
sendToLog "Error: Unable to find requested software updates, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to find requested software updates, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "SPACE" ]]; then
sendToASULog "Error: Not enough free disk space to download software updates, install now workflow can not continue."
sendToLog "Error: Not enough free disk space to download software updates, install now workflow can not continue."
sendToStatus "Inactive Error: Not enough free disk space to download software updates, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "FAILED" ]]; then
sendToASULog "Error: Failed to download & prepare updates, install now workflow can not continue."
sendToLog "Error: Failed to download & prepare updates, install now workflow can not continue."
sendToStatus "Inactive Error: Failed to download & prepare updates, install now workflow can not continue."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Download of software updates timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Download of software updates timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Download of software updates timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
fi
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Download of software updates failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "CONNECT" ]]; then
sendToASULog "Error: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
sendToLog "Error: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "NOUPDATE" ]]; then
sendToASULog "Error: Unable to find requested software updates, trying again in $deferSECONDS seconds."
sendToLog "Error: Unable to find requested software updates, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to find requested software updates, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "SPACE" ]]; then
sendToASULog "Error: Not enough free disk space to download software updates, trying again in $deferSECONDS seconds."
sendToLog "Error: Not enough free disk space to download software updates, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Not enough free disk space to download software updates, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "FAILED" ]]; then
sendToASULog "Error: Failed to download & prepare updates, trying again in $deferSECONDS seconds."
sendToLog "Error: Failed to download & prepare updates, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Failed to download & prepare updates, trying again in $deferSECONDS seconds."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Download of software updates timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Download of software updates timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Download of software updates timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
fi
makeLaunchDaemonCalendar
fi
fi
}
# Download macOS installer via $eraseInstallSCRIPT, and also save results to $superLOG, $installerLOG, and $superPLIST.
downloadMacOSInstaller() {
sendToLog "erase-install.sh: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET download installer workflow, check $installerLOG for more detail."
sendToStatus "Running: erase-install.sh: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET download installer workflow."
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET INSTALLER START ****"
# Background the erase-install download process and send to $installerLOG.
if [[ "$betaWORKFLOW" != "FALSE" ]]; then
"$eraseInstallSCRIPT" --update --seed "$betaWORKFLOW" --build "$macOSInstallerBuildTARGET" --move >> "$installerLOG" 2>&1 &
eraseInstallPID=$!
else # Standard non-beta workflow.
"$eraseInstallSCRIPT" --update --build "$macOSInstallerBuildTARGET" --move >> "$installerLOG" 2>&1 &
eraseInstallPID=$!
fi
# Watch $installerLOG while waiting for the erase-install download process to complete.
# Note this while read loop has a timeout based on $initialStartTimeoutSECONDS then changes to $macOSInstallerDownloadTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
workflowPHASE=""
workflowCompletePERCENT=0
workflowPreviousCompletePERCENT=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'Cannot continue.' ) -gt 0 ]] || [[ $(echo "$macOSInstallerLIST" | grep -c 'exit code: 1') -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'installinstallmacos.py --workdir') -gt 0 ]]; then
sendToLog "erase-install.sh: Install $macOSInstallerNameTARGET.app is downloading..."
sendToInstallerLog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$macOSInstallerDownloadTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowPHASE="DOWNLOADING"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ "$workflowPHASE" == "DOWNLOADING" ]] && [[ $(echo "$logLINE" | grep -c '^[1-9]') -gt 0 ]] && [[ $(echo "$logLINE" | grep -c 'M') -gt 0 ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | awk '{print $1}')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 100 ]]; then
sendToEchoReplaceLine "Install $macOSInstallerNameTARGET.app download progress: 100%\n"
sendToLog "erase-install.sh: Install $macOSInstallerNameTARGET.app download complete, now preparing..."
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="DONE"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
sendToEchoReplaceLine "Install $macOSInstallerNameTARGET.app download progress: $workflowCompletePERCENT%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c 'Invoking --move option') -gt 0 ]]; then
sendToLog "erase-install.sh: Install $macOSInstallerNameTARGET.app is prepared, now moving to /Applications..."
sendToInstallerLog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Installation moved') -gt 0 ]]; then
sendToLog "erase-install.sh: Install $macOSInstallerNameTARGET.app moved to /Applications."
sendToInstallerLog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Valid installer') -gt 0 ]]; then
sendToLog "erase-install.sh: Install $macOSInstallerNameTARGET.app already found in /Applications."
sendToInstallerLog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'finish') -gt 0 ]]; then
sendToInstallerLog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$installerLOG" | tr -u 'G' '\n')
# If the erase-install download process completed, then prepare for future installation.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]] && [[ "$workflowTIMEOUT" == "FALSE" ]]; then
if [[ -d "/Applications/Install $macOSInstallerNameTARGET.app" ]]; then
sendToLog "Status: Install $macOSInstallerNameTARGET.app Gatekeeper validation..."
sendToInstallerLog "Status: Install $macOSInstallerNameTARGET.app Gatekeeper validation."
startosinstallRESULTS=$("/Applications/Install $macOSInstallerNameTARGET.app/Contents/Resources/startosinstall" --usage 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: startosinstallRESULTS is:\n$startosinstallRESULTS"
if [[ $(echo "$startosinstallRESULTS" | grep -c 'Usage: startosinstall') -gt 0 ]]; then
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD MACOS INSTALLER COMPLETE ****"
sendToLog "Status: macOS installer is now available at: /Applications/Install $macOSInstallerNameTARGET.app"
defaults write "$superPLIST" macOSInstallerDownloadVersion -string "$macOSInstallerVersionTARGET"
defaults write "$superPLIST" macOSInstallerDownloadName -string "$macOSInstallerNameTARGET"
macOSInstallerDownloadREQUIRED="FALSE"
[[ "$onlyDownloadOPTION" == "TRUE" ]] && onlyDownloadCOMPLETE="TRUE"
else # The installer can't be verified, so clean-up and try again later.
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD MACOS INSTALLER FAILURE ****"
sendToInstallerLog "Warning: macOS installer failed Gatekeeper validation, removing installer: /Applications/Install $macOSInstallerNameTARGET.app"
sendToLog "Warning: macOS installer failed Gatekeeper validation, removing installer: /Applications/Install $macOSInstallerNameTARGET.app"
rm -Rf "/Applications/Install $macOSInstallerNameTARGET.app" > /dev/null 2>&1
defaults delete "$superPLIST" macOSInstallerDownloadVersion 2> /dev/null
defaults delete "$superPLIST" macOSInstallerDownloadName 2> /dev/null
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download could not be verified, install now workflow can not continue."
sendToLog "Error: macOS installer download could not be verified, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer download could not be verified, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToInstallerLog "Error: macOS installer download could not be verified, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer download could not be verified, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer download could not be verified, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
else # The installer can't be found, so clean-up and try again later.
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD MACOS INSTALLER FAILURE ****"
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download could not be found, install now workflow can not continue."
sendToLog "Error: macOS installer download could not be found, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer download could not be found, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToInstallerLog "Error: macOS installer download could not be found, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer download could not be found, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer download could not be found, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
else # The erase-install download workflow failed so clean-up and try again later.
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD MACOS INSTALLER FAILED ****"
kill -9 "$eraseInstallPID" > /dev/null 2>&1
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed to download, install now workflow can not continue."
sendToLog "Error: macOS installer failed to download, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer failed to download, install now workflow can not continue."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: macOS installer download timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer download timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
fi
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer download failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed to download, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer failed to download, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer failed to download, trying again in $deferSECONDS seconds."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer download timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer download timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer download timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
fi
makeLaunchDaemonCalendar
fi
fi
}
# This function contains logic to determine the correct download behavior based on system condition and specified options.
downloadMacOSWorkflow() {
# Check to make sure system has enough available free storage space.
checkAvailableStorage
if [[ "$storageREADY" == "FALSE" ]]; then
if [[ "$currentUserNAME" == "FALSE" ]]; then
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Current available storage is at $availableStorageGB GBs which is below the $requiredStorageGB GBs that is required for download. No active user is logged in so trying again in $deferSECONDS seconds."
sendToStatus "Inactive Error: Current available storage is at $availableStorageGB GBs which is below the $requiredStorageGB GBs that is required for download. No active user is logged in so trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
else # A normal user is currently logged in.
notifyStorage
fi
fi
# If the workflow made it this far there is sufficient free space to continue with download.
if [[ "$testModeOPTION" != "TRUE" ]]; then
onlyDownloadCOMPLETE="FALSE"
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then # macOS 12.3 or newer can upgrade via softwareupdate, unless using MDM workflow.
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then
[[ "$installNowOPTION" == "TRUE" ]] && notifyInstallNowDownload
downloadMacOSSoftwareUpdate
else
sendToLog "Status: Previously downloaded macOS upgrade is prepared: $macOSSoftwareUpdateDownloadLABEL"
fi
else # Systems older than macOS 12.3 or using the MDM workflow upgrade via installer.
if [[ "$macOSInstallerDownloadREQUIRED" == "TRUE" ]]; then
[[ "$installNowOPTION" == "TRUE" ]] && notifyInstallNowDownload
downloadMacOSInstaller
else
sendToLog "Status: Previously downloaded $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installer is available at: /Applications/Install $macOSInstallerNameTARGET.app"
fi
fi
else # Only macOS updates are available.
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then
[[ "$installNowOPTION" == "TRUE" ]] && notifyInstallNowDownload
downloadMacOSSoftwareUpdate
else
sendToLog "Status: Previously downloaded macOS update is prepared: $macOSSoftwareUpdateDownloadLABEL"
fi
fi
else # Test mode.
if [[ "$installNowOPTION" == "TRUE" ]]; then
notifyInstallNowDownload
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for install now download notification..."
sleep "$testModeTimeoutSECONDS"
else
sendToLog "Test Mode: Ignoring macOS download workflow."
fi
fi
}
# Install any optional $policyTRIGGERS.
runJamfPolicies() {
sendToLog "Status: Starting Jamf Policy triggers. Use --verbose-mode or check /var/log/jamf.log for more detail..."
sendToStatus "Running: Starting Jamf Policy triggers."
oldIFS="$IFS"; IFS=','
read -r -a triggerARRAY <<< "$policyTRIGGERS"
for trigger in "${triggerARRAY[@]}"; do
if [[ "$testModeOPTION" != "TRUE" ]]; then
sendToLog "Status: Jamf Policy with Trigger \"$trigger\" is starting..."
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
jamfRESULT=$("$jamfBINARY" policy -event "$trigger" -verbose 2>&1)
jamfRETURN=$?
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRESULT is:\n$jamfRESULT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRETURN is: $jamfRETURN"
else
"$jamfBINARY" policy -event "$trigger" > /dev/null 2>&1
jamfRETURN=$?
fi
if [ $jamfRETURN -ne 0 ]; then
sendToLog "Error: Jamf Policy with Trigger \"$trigger\" failed!"; jamfERROR="TRUE"
else
sendToLog "Status: Jamf Policy with Trigger \"$trigger\" was successful."
fi
else
sendToLog "Test Mode: Skipping Jamf Policy with Trigger: $trigger."
fi
done
IFS="$oldIFS"
if [[ "$testModeOPTION" != "TRUE" ]]; then
if [[ "$jamfERROR" != "TRUE" ]]; then
sendToLog "Status: All Jamf Policies completed, deleting local policy triggers preference."
defaults delete "$superPLIST" PolicyTriggers 2> /dev/null
else
sendToLog "Status: Some Jamf Policies failed, not deleting local policy triggers preference."
fi
else
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the restart notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
}
# MARK: *** Install & Restart Workflows ***
################################################################################
# Install only recommended (non-system) updates via the softwareupdate command, and also save results to $superLOG and $asuLOG.
installRecommendedSoftwareUpdates() {
sendToLog "softwareupdate: Starting recommended (non-system) software installation workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting recommended (non-system) software installation workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL RECOMMENDED (NON-SYSTEM) UPDATES SOFTWAREUPDATE START ****"
oldIFS="$IFS"; IFS=$' '
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: recommendedSoftwareUpdateLABELS[] is:\n${recommendedSoftwareUpdateLABELS[*]}"
# The update process is backgrounded and is watched via a while loop later on. Also note the different requirements between macOS versions.
if [[ $macOSMAJOR -ge 12 ]]; then
if [[ "$currentUserNAME" == "FALSE" ]]; then
sudo -i softwareupdate --install "${recommendedSoftwareUpdateLABELS[@]}" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Local user is logged in.
launchctl asuser "$currentUserUID" sudo -i softwareupdate --install "${recommendedSoftwareUpdateLABELS[@]}" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
elif [[ $macOSMAJOR -eq 11 ]]; then
softwareupdate --install "${recommendedSoftwareUpdateLABELS[@]}" --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # macOS 10.X
softwareupdate --install "${recommendedSoftwareUpdateLABELS[@]}" >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
# Watch $asuLOG while waiting for the softwareupdate installation workflow to complete.
# Note this while read loop has a timeout based on $initialStartTimeoutSECONDS then changes to $softwareUpdateRecommendedTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
softwareUpdateInstalledTITLES=()
softwareUpdateERROR="TRUE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c "Can’t connect" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c "Couldn't communicate" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'No such update' ) -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Downloading') -gt 0 ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/://' | awk -F 'Downloading ' '{print $2}')
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE is downloading..."
sendToASULog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$softwareUpdateRecommendedTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Downloaded') -gt 0 ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/://' | awk -F 'Downloaded ' '{print $2}')
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE download complete."
workflowTimeoutSECONDS=$softwareUpdateRecommendedTimeoutSECONDS
sendToASULog "**** TIMESTAMP ****"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Done with') -gt 0 ]]; then
softwareUpdateInstalledTITLE=$(echo "$logLINE" | sed -e 's/://' | awk -F 'Done with ' '{print $2}')
sendToLog "softwareupdate: $softwareUpdateInstalledTITLE installed."
softwareUpdateInstalledTITLES+=("${softwareUpdateInstalledTITLE}")
sendToASULog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Done.') -gt 0 ]]; then
sendToASULog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$asuLOG")
# If the softwareupdate installation workflow completed, then validate and collect information.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]] && [[ "$workflowTIMEOUT" == "FALSE" ]]; then
oldIFS="$IFS"; IFS=$'\n'
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: recommendedSoftwareUpdateTITLES[] is:\n${recommendedSoftwareUpdateTITLES[*]}"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateInstalledTITLES[] is:\n${softwareUpdateInstalledTITLES[*]}"
if [[ ! $(echo -e "${recommendedSoftwareUpdateTITLES[*]}\n${softwareUpdateInstalledTITLES[*]}" | sort | uniq -u) ]]; then
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION RECOMMENDED (NON-SYSTEM) UPDATES SOFTWAREUPDATE COMPLETED ****"
softwareUpdateERROR="FALSE"
else # The expected $recommendedSoftwareUpdateTITLES[] did not match the $softwareUpdateInstalledTITLES[].
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION RECOMMENDED (NON-SYSTEM) UPDATES SOFTWAREUPDATE INCOMPLETE ****"
sendToASULog "Error: Installation of recommended (non-system) software updates did not complete."
sendToLog "Error: Installation of recommended (non-system) software updates did not complete."
fi
else # The softwareupdate installation workflow failed.
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION RECOMMENDED (NON-SYSTEM) UPDATES SOFTWAREUPDATE FAILED ****"
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of recommended (non-system) software updates failed to start after $workflowTimeoutSECONDS seconds."
sendToLog "Error: Installation of recommended (non-system) software updates failed to start after $workflowTimeoutSECONDS seconds."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToASULog "Error: Unable to reach Apple Software Update server."
sendToLog "Error: Unable to reach Apple Software Update server."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of recommended (non-system) software updates timed out after $workflowTimeoutSECONDS seconds."
sendToLog "Error: Installation of recommended (non-system) software updates timed out after $workflowTimeoutSECONDS seconds."
fi
kill -9 "$softwareupdatePID" > /dev/null 2>&1
kickSoftwareUpdateD
fi
IFS="$oldIFS"
}
# Install macOS updates via the softwareupdate command, and also save results to $superLOG, $asuLOG, and $superPLIST.
installMacOSSoftwareUpdate() {
if [[ "$testModeOPTION" != "TRUE" ]]; then # Not in test mode.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # macOS 12.3 or newer upgrade via softwareupdate.
softwareUpdateLabelTARGET="$macOSSoftwareUpgradeLabelTARGET"
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then # If no $currentUserNAME then the sytem update was not pre-downloaded.
sendToLog "softwareupdate: Starting $macOSSoftwareUpgradeLabelTARGET download and upgrade workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting $macOSSoftwareUpgradeLabelTARGET download and upgrade workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD AND UPGRADE $macOSSoftwareUpgradeLabelTARGET SOFTWAREUPDATE START ****"
else
sendToLog "softwareupdate: Starting $macOSSoftwareUpgradeLabelTARGET upgrade workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting $macOSSoftwareUpgradeLabelTARGET upgrade workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE $macOSSoftwareUpgradeLabelTARGET SOFTWAREUPDATE START ****"
fi
else # Older than macOS 12 and/or only macOS updates available.
softwareUpdateLabelTARGET="$macOSSoftwareUpdateLABEL"
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then # If no $currentUserNAME then the sytem update was not pre-downloaded.
sendToLog "softwareupdate: Starting $macOSSoftwareUpdateLABEL download and update workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting $macOSSoftwareUpdateLABEL download and update workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION DOWNLOAD AND UPDATE $macOSSoftwareUpdateLABEL SOFTWAREUPDATE START ****"
else
sendToLog "softwareupdate: Starting $macOSSoftwareUpdateLABEL update workflow, check $asuLOG for more detail."
sendToStatus "Running: softwareupdate: Starting $macOSSoftwareUpdateLABEL update workflow."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE $macOSSoftwareUpdateLABEL SOFTWAREUPDATE START ****"
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateLabelTARGET is: $softwareUpdateLabelTARGET"
# The update/upgrade process is backgrounded and is watched via while loops later on. Also note the different requirements between macOS versions.
if [[ $macOSMAJOR -ge 13 ]]; then # macOS 13+
if [[ "$currentUserNAME" == "FALSE" ]]; then # Local user not is logged in.
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
echo "$installPASSWORD" | sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license --user "$installACCOUNT" --stdinpass >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
else # Local user is logged in.
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
echo "$installPASSWORD" | launchctl asuser "$currentUserUID" sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license --user "$installACCOUNT" --stdinpass >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
launchctl asuser "$currentUserUID" sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
fi
elif [[ $macOSMAJOR -ge 12 ]]; then # macOS 12
if [[ "$currentUserNAME" == "FALSE" ]]; then # Local user not is logged in.
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license --user "$installACCOUNT" --stdinpass "$installPASSWORD" >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
else # Local user is logged in.
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
launchctl asuser "$currentUserUID" sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license --user "$installACCOUNT" --stdinpass "$installPASSWORD" >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
launchctl asuser "$currentUserUID" sudo -u root softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
fi
elif [[ $macOSMAJOR -eq 11 ]]; then # macOS 11
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
echo ' ' | softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
else # Intel.
softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan --agree-to-license >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
else # macOS 10.x
softwareupdate --install "$softwareUpdateLabelTARGET" --restart --force --no-scan >> "$asuLOG" 2>&1 &
softwareupdatePID=$!
fi
disown -a
workflowTIMEOUT="TRUE"
# Watch $asuLOG while waiting for the softwareupdate installation workflow to complete.
# Note this while read loop has a timeout based on $initialStartTimeoutSECONDS then changes to $softwareUpdateTimeoutSECONDS or $softwareUpdateLegacyTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
workflowPHASE=""
workflowCompletePERCENT=0
workflowPreviousCompletePERCENT=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c "Can’t connect" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c "Couldn't communicate" ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'No such update' ) -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'Failed to download' ) -gt 0 ]]; then
workflowStartTIMEOUT="FALSE"
break
elif [[ $macOSMAJOR -lt 11 ]] && [[ $(echo "$logLINE" | grep -c 'Finding available software') -gt 0 ]]; then
sendToLog "softwareupdate: The download workflow has started but detailed progress is not available for systems older than macOS 11..."
workflowTimeoutSECONDS=$softwareUpdateLegacyTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartFAIL="FALSE"
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading') -gt 0 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading:') -eq 0 ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/Downloading //')
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE is downloading..."
sendToASULog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$softwareUpdateTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartFAIL="FALSE"
[[ $(echo "$softwareUpdateDownloadTITLE" | grep -c 'macOS') -gt 0 ]] && workflowPHASE="DOWNLOADING"
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading:') -gt 0 ]] && [[ "$workflowPHASE" == "DOWNLOADING" ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | sed -e 's/Downloading: //' -e 's/\.[0-9][0-9]//' | tr -d '\n' | tr -d '\r')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 60 ]]; then
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE download progress: 100%\n"
sendToLog "softwareupdate: $softwareUpdateDownloadTITLE download complete, now preparing..."
sendToASULog "**** TIMESTAMP ****"
workflowPHASE="PREPARING"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$( (echo "$workflowCompletePERCENT * 1.69" | bc ) | cut -d '.' -f1)
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE download progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $macOSMAJOR -ge 11 ]] && [[ $(echo "$logLINE" | grep -c 'Downloading:') -gt 0 ]] && [[ "$workflowPHASE" == "PREPARING" ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | sed -e 's/Downloading: //' -e 's/\.[0-9][0-9]//' | tr -d '\n' | tr -d '\r')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 100 ]]; then
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE preparing progress: 100%\n"
sendToASULog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
workflowTIMEOUT="FALSE"
break
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$(((workflowCompletePERCENT-60)*2))
sendToEchoReplaceLine "$softwareUpdateDownloadTITLE preparing progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c 'Downloaded') -gt 0 ]]; then
softwareUpdateDownloadTITLE=$(echo "$logLINE" | sed -e 's/://' -e 's/Downloaded //')
sendToASULog "**** TIMESTAMP ****"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
workflowTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$asuLOG" | tr -u '%' '\n')
# If the softwareupdate installation workflow completed, then prepare for restart.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]] && [[ "$workflowTIMEOUT" == "FALSE" ]]; then
sendToLog "softwareupdate: macOS update/upgrade is prepared and ready for restart."
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS SOFTWAREUPDATE COMPLETED ****"
installRestartComplete
else # The softwareupdate download workflow failed so clean-up and try again later.
sendToASULog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS SOFTWAREUPDATE FAILED ****"
kill -9 "$softwareupdatePID" > /dev/null 2>&1
kickSoftwareUpdateD
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToASULog "Error: Unable to reach Apple Software Update server, install now workflow can not continue."
sendToLog "Error: Unable to reach Apple Software Update server, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to reach Apple Software Update server, install now workflow can not continue."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
fi
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Installation of macOS update/upgrade failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToASULog "Error: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
sendToLog "Error: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to reach Apple Software Update server, trying again in $deferSECONDS seconds."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToASULog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
fi
[[ "$currentUserNAME" != "FALSE" ]] && notifyFailure
makeLaunchDaemonCalendar
fi
fi
else # Test Mode.
sendToLog "Test Mode: Skipping the macOS softwareupdate workflow."
if [[ "$currentUserNAME" != "FALSE" ]]; then
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the restart notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
# Reset various items after test macOS update is complete.
restartZeroDay
restartDeferralCounters
fi
}
# Install macOS update/upgrade via MDM push command, and also save results to $superLOG, $mdmCommandLOG, $mdmWorkflowLOG, and $superPLIST.
installMacOSMDM() {
if [[ "$testModeOPTION" != "TRUE" ]]; then # Not in test mode.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSSoftwareUpdateDownloadREQUIRED is: $macOSSoftwareUpdateDownloadREQUIRED"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMDMFAILOVER is: $userAuthMDMFAILOVER"
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # macOS upgrade via installer, further the installer is always pre-downloaded.
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToLog "MDM: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install workflow with user authenticated failover."
sendToStatus "Running: MDM: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install workflow with user authenticated failover."
else
sendToLog "MDM: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install workflow."
sendToStatus "Running: MDM: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install workflow."
fi
sendToMDMCommandLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET MDM START ****"
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET MDM START ****"
else # macOS update.
if [[ "$macOSSoftwareUpdateDownloadREQUIRED" == "TRUE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToLog "MDM: Starting macOS $macOSSoftwareUpdateVERSION download and update workflow with user authenticated failover."
sendToStatus "Running: MDM: Starting macOS $macOSSoftwareUpdateVERSION download and update workflow with user authenticated failover."
else
sendToLog "MDM: Starting macOS $macOSSoftwareUpdateVERSION download and update workflow."
sendToStatus "Running: MDM: Starting macOS $macOSSoftwareUpdateVERSION download and update workflow."
fi
sendToMDMCommandLog "**** DOWNLOAD AND UPDATE S.U.P.E.R.M.A.N. MACOS $macOSSoftwareUpdateVERSION MDM START ****"
sendToMDMWorkflowLog "**** DOWNLOAD AND UPDATE S.U.P.E.R.M.A.N. MACOS $macOSSoftwareUpdateVERSION MDM START ****"
else
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToLog "MDM: Starting macOS $macOSSoftwareUpdateVERSION update workflow with user authenticated failover."
sendToStatus "Running: MDM: Starting macOS $macOSSoftwareUpdateVERSION update workflow with user authenticated failover."
else
sendToLog "MDM: Starting macOS $macOSSoftwareUpdateVERSION update workflow."
sendToStatus "Running: MDM: Starting macOS $macOSSoftwareUpdateVERSION update workflow."
fi
sendToMDMCommandLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE MACOS $macOSSoftwareUpdateVERSION MDM START ****"
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE MACOS $macOSSoftwareUpdateVERSION MDM START ****"
fi
fi
sendToLog "MDM: check $mdmCommandLOG and $mdmWorkflowLOG for more detail."
# For macOS 11 or newer restarting the softwareupdate processes helps to prevent macOS updates from hanging.
kickSoftwareUpdateD
# This pre-flights the MDM query locally and may also be useful for troubleshooting.
availableOSUPDATES=$(/usr/libexec/mdmclient AvailableOSUpdates 2> /dev/null)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: availableOSUPDATES is: $availableOSUPDATES"
# Validate Jamf Pro API token.
checkJamfProServerToken
# Start log streaming for MDM push commands and send to $mdmCommandLOG.
log stream --style compact --predicate 'subsystem == "com.apple.ManagedClient" AND category == "HTTPUtil"' >> "$mdmCommandLOG" &
mdmCommandStreamPID=$!
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
sendToLog "Verbose Mode: Starting debug log for MDM client command progress at: $mdmCommandDebugLOG"
log stream --style compact --predicate 'subsystem == "com.apple.ManagedClient"' >> "$mdmCommandDebugLOG" &
mdmCommandDebugPID=$!
fi
# Start log streaming for MDM update/upgrade progress and send to $mdmWorkflowLOG.
log stream --style compact --predicate 'process == "softwareupdated" AND composedMessage CONTAINS "Reported progress"' >> "$mdmWorkflowLOG" &
mdmWorkflowStreamPID=$!
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
sendToLog "Verbose Mode: Starting debug log for MDM update/upgrade workflow progress at: $mdmWorkflowDebugLOG"
log stream --style compact --predicate 'process == "softwareupdated"' >> "$mdmWorkflowDebugLOG" &
mdmWorkflowDebugPID=$!
fi
# Send the Jamf Pro API command to update and restart via MDM.
jamfAPIURL="${jamfSERVER}api/v1/macos-managed-software-updates/send-updates"
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # macOS upgrade.
mdmWorkflowTYPE="INSTALLER"
if [[ "$betaWORKFLOW" != "FALSE" ]]; then
jamfJSON='{ "deviceIds": ["'${jamfProID}'"], "applyMajorUpdate": true, "skipVersionVerification": true, "updateAction": "DOWNLOAD_AND_INSTALL" }'
else # Standard non-beta workflow.
jamfJSON='{ "deviceIds": ["'${jamfProID}'"], "applyMajorUpdate": true, "updateAction": "DOWNLOAD_AND_INSTALL" }'
fi
else # macOS update.
mdmWorkflowTYPE="UPDATE"
if [[ "$betaWORKFLOW" != "FALSE" ]] || [[ "$softwareUpdateRSR" == "TRUE" ]]; then
jamfJSON='{ "deviceIds": ["'${jamfProID}'"], "applyMajorUpdate": false, "skipVersionVerification": true, "updateAction": "DOWNLOAD_AND_INSTALL", "forceRestart": true }'
else # Standard non-beta workflow.
jamfJSON='{ "deviceIds": ["'${jamfProID}'"], "applyMajorUpdate": false, "updateAction": "DOWNLOAD_AND_INSTALL", "forceRestart": true }'
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: mdmWorkflowTYPE is: $mdmWorkflowTYPE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfAPIURL is: $jamfAPIURL"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfJSON is: $jamfJSON"
commandRESULT=$(curl --header "Authorization: Bearer ${jamfProTOKEN}" --header "Content-Type: application/json" --write-out "%{http_code}" --silent --show-error --request POST --url "${jamfAPIURL}" --data "${jamfJSON}")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is:\n$commandRESULT"
# If the Jamf Pro API command was successfully created, monitor the update progress.
if [[ $(echo "$commandRESULT" | grep -c '200') -gt 0 ]] || [[ $(echo "$commandRESULT" | grep -c '201') -gt 0 ]]; then
sendToLog "MDM: Successful macOS update/upgrade command request."
sendBlankPush
mdmTIMEOUT="TRUE"
mdmFAIL="TRUE"
# Some helpfull logging while waiting for Jamf Pro's mandatory 5 minute delay. Note this while read loop has a timeout based on $mdmTimeoutSECONDS.
while read -t $mdmTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Error') -gt 0 ]]; then
sendToLog "MDM: Workflow error detected."
sendToMDMCommandLog "**** TIMESTAMP ****"
mdmTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Acknowledged(ScheduleOSUpdateScan)') -gt 0 ]]; then
sendToLog "MDM: Received push command \"ScheduleOSUpdateScan\", checking back after Jamf Pro's mandatory 5 minute delay..."
sendToMDMCommandLog "**** TIMESTAMP ****"
mdmTIMEOUT="FALSE"
mdmFAIL="FALSE"
pkill -P $$ tail
break
fi
done < <(tail -n1 -F "$mdmCommandLOG")
# Only continue workflow if it did not timeout or fail.
if [[ "$mdmTIMEOUT" == "FALSE" ]] && [[ "$mdmFAIL" == "FALSE" ]]; then
timerEND=300
while [[ $timerEND -ge 0 ]]; do
sendToEchoReplaceLine "Waiting for Jamf Pro's mandatory 5 minute delay: -$(date -u -r $timerEND +%M:%S)"
timerEND=$((timerEND - 1))
sleep 1
done
sendToEchoReplaceLine "Waiting for Jamf Pro's mandatory 5 minute delay: 00:00\n)"
sendToLog "MDM: Jamf Pro's mandatory 5 minute delay should be complete, sending Blank Push..."
sendBlankPush
mdmTIMEOUT="TRUE"
mdmFAIL="TRUE"
if [[ "$currentUserNAME" != "FALSE" ]]; then
if [[ "$mdmWorkflowTYPE" == "INSTALLER" ]]; then
prepareTimeEstimateDISPLAY="20-25"
notifyPrepare
else
notifyRestart
fi
fi
# Watch $mdmCommandLOG while waiting for the MDM workflow to complete. Note this while read loop has a timeout based on $mdmTimeoutSECONDS.
while read -t $mdmTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Error') -gt 0 ]]; then
sendToLog "MDM: Workflow error detected."
sendToMDMCommandLog "**** TIMESTAMP ****"
mdmTIMEOUT="FALSE"
break
elif [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Idle\]') -gt 0 ]]; then
sendToLog "MDM: Received blank push."
sendToMDMCommandLog "**** TIMESTAMP ****"
elif [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Acknowledged(AvailableOSUpdates)') -gt 0 ]]; then
sendToLog "MDM: Received push command \"AvailableOSUpdates\"."
sendToMDMCommandLog "**** TIMESTAMP ****"
elif [[ $(echo "$logLINE" | grep -c 'Received HTTP response (200) \[Acknowledged(ScheduleOSUpdate)') -gt 0 ]]; then
kill -9 "$mdmCommandStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmCommandDebugPID" > /dev/null 2>&1
sendToMDMCommandLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM PUSH COMPLETED ****"
sendToLog "MDM: Received push command \"ScheduleOSUpdate\", local update/upgrade workflow should start soon..."
mdmTIMEOUT="FALSE"
mdmFAIL="FALSE"
break
fi
done < <(tail -n1 -F "$mdmCommandLOG")
fi
# If the MDM push commands did not complete after $mdmTimeoutSECONDS seconds, then clean-up and try again later.
if [[ "$mdmTIMEOUT" == "TRUE" ]] && [[ "$mdmFAIL" == "TRUE" ]]; then
sendToMDMCommandLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM PUSH FAILED ****"
kill -9 "$mdmCommandStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmCommandDebugPID" > /dev/null 2>&1
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM PUSH FAILED ****"
kill -9 "$mdmWorkflowStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmWorkflowDebugPID" > /dev/null 2>&1
kickSoftwareUpdateD
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
if [[ "$mdmTIMEOUT" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
elif [[ "$mdmFAIL" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow"
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow"
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow"
fi
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else # No user authentication MDM failover option.
if [[ "$mdmTIMEOUT" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, install now workflow can not continue."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, install now workflow can not continue."
elif [[ "$mdmFAIL" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
sendToStatus "Inactive Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
fi
notifyInstallNowFailure
errorExit
fi
else # Default super workflow.
if [[ "$currentUserNAME" != "FALSE" ]] && [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
if [[ "$mdmTIMEOUT" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, failing over to user authenticated workflow."
elif [[ "$mdmFAIL" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
fi
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else # No user authentication MDM failover option.
deferSECONDS="$errorDeferSECONDS"
if [[ "$mdmTIMEOUT" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Push workflow for macOS update/upgrade via MDM timed out after $mdmTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
elif [[ "$mdmFAIL" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
sendToMDMWorkflowLog "Error: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
fi
[[ "$currentUserNAME" != "FALSE" ]] && notifyFailure
makeLaunchDaemonCalendar
fi
fi
fi
# Watch $mdmWorkflowLOG while waiting for the update/upgrade workflow to complete.
# Note this while read loop has a timeout based on $mdmTimeoutSECONDS then may change to $mdmWorkflowTimeoutSECONDS.
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$mdmTimeoutSECONDS
workflowPHASE=""
workflowCompletePERCENT=0
workflowPreviousCompletePERCENT=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: mdmWorkflowTYPE is: $mdmWorkflowTYPE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
if [[ "$mdmWorkflowTYPE" == "UPGRADE" ]] || [[ "$mdmWorkflowTYPE" == "UPDATE" ]]; then
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'phase:PREFLIGHT') -gt 0 ]]; then
if [[ "$workflowPHASE" != "PREFLIGHT" ]] && [[ "$workflowPHASE" != "DOWNLOADING" ]] && [[ "$workflowPHASE" != "PREPARING" ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToLog "MDM: $macOSSoftwareUpgradeLabelTARGET upgrade preflight..."
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToLog "MDM: $macOSSoftwareUpdateLABEL update preflight..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$mdmWorkflowTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowPHASE="PREFLIGHT"
fi
elif [[ $(echo "$logLINE" | grep -c 'phase:DOWNLOADING_UPDATE') -gt 0 ]]; then
if [[ "$workflowPHASE" != "DOWNLOADING" ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToLog "MDM: $macOSSoftwareUpgradeLabelTARGET upgrade is downloading..."
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToLog "MDM: $macOSSoftwareUpdateLABEL update is downloading..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$mdmWorkflowTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowPHASE="DOWNLOADING"
fi
workflowCompletePERCENT=$(echo "$logLINE" | awk '{print $17}' | sed -e 's/portionComplete:0.//' | cut -c 1-2)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 60 ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpgradeLabelTARGET upgrade download progress: 100%\n"
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpdateLABEL update download progress: 100%\n"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$( (echo "$workflowCompletePERCENT * 1.69" | bc ) | cut -d '.' -f1)
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpgradeLabelTARGET upgrade download progress: $workflowCompleteDISPLAY%"
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpdateLABEL update download progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c 'phase:PREPARING_UPDATE') -gt 0 ]]; then
if [[ "$workflowPHASE" != "PREPARING" ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToLog "MDM: $macOSSoftwareUpgradeLabelTARGET upgrade download complete, now preparing..."
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToLog "MDM: $macOSSoftwareUpdateLABEL update download complete, now preparing..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$mdmWorkflowTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowPHASE="PREPARING"
fi
workflowCompletePERCENT=$(echo "$logLINE" | awk '{print $17}' | sed -e 's/portionComplete:0.//' | cut -c 1-2)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 98 ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpgradeLabelTARGET upgrade preparing progress: 100%\n"
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToLog "MDM: $macOSSoftwareUpgradeLabelTARGET upgrade is downloaded and prepared, system restart is soon..."
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpdateLABEL update preparing progress: 100%\n"
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToLog "MDM: $macOSSoftwareUpdateLABEL update is downloaded and prepared, system restart is soon..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
workflowCompleteDISPLAY=$(((workflowCompletePERCENT-60)*2))
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpgradeLabelTARGET upgrade preparing progress: $workflowCompleteDISPLAY%"
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToEchoReplaceLine "$macOSSoftwareUpdateLABEL update preparing progress: $workflowCompleteDISPLAY%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c 'phase:PREPARED_COMMITTING_STASH') -gt 0 ]]; then
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
workflowPHASE="DONE"
break
fi
done < <(tail -n1 -F "$mdmWorkflowLOG")
else # $mdmWorkflowTYPE == "INSTALLER"
# This while loop is broken into sections to allow for the notifyPrepare function to update. Putting this function inside the while loop breaks the tail.
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'phase:PREFLIGHT') -gt 0 ]]; then
sendToLog "MDM: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installer preflight..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTimeoutSECONDS=$mdmWorkflowTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowTIMEOUT="FALSE"
workflowPHASE="PREFLIGHT"
break
fi
done < <(tail -n1 -F "$mdmWorkflowLOG")
if [[ "$workflowTIMEOUT" == "FALSE" ]]; then
workflowTIMEOUT="TRUE"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'phase:DOWNLOADING_SFR') -gt 0 ]]; then
sendToLog "MDM: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET downloading additional items..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowPHASE="DOWNLOADING"
elif [[ $(echo "$logLINE" | grep -c 'phase:PREPARING_UPDATE') -gt 0 ]]; then
sendToLog "MDM: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installer preparing..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
workflowPHASE="PREPARING"
break
fi
done < <(tail -n1 -F "$mdmWorkflowLOG")
fi
if [[ "$workflowTIMEOUT" == "FALSE" ]] && [[ "$currentUserNAME" != "FALSE" ]]; then
prepareTimeEstimateDISPLAY="10-15"
notifyPrepare
fi
if [[ "$workflowTIMEOUT" == "FALSE" ]]; then
workflowTIMEOUT="TRUE"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'phase:PREPARED') -gt 0 ]]; then
sendToLog "MDM: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installer is prepared, system restart is soon..."
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
workflowPHASE="DONE"
break
fi
done < <(tail -n1 -F "$mdmWorkflowLOG")
fi
{ [[ "$workflowTIMEOUT" == "FALSE" ]] && [[ "$currentUserNAME" != "FALSE" ]]; } && notifyRestart
if [[ "$workflowTIMEOUT" == "FALSE" ]]; then
workflowTIMEOUT="TRUE"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'phase:ACCEPTED') -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'phase:APPLYING') -gt 0 ]] || [[ $(echo "$logLINE" | grep -c 'phase:APPLYING') -gt 0 ]]; then
sendToMDMWorkflowLog "**** TIMESTAMP ****"
workflowTIMEOUT="FALSE"
workflowPHASE="DONE"
break
fi
done < <(tail -n1 -F "$mdmWorkflowLOG")
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTIMEOUT is: $workflowTIMEOUT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowPHASE is: $workflowPHASE"
# If the macOS update/upgrade completed, then prepare for restart.
if [[ "$workflowTIMEOUT" == "FALSE" ]]; then
[[ "$mdmWorkflowTYPE" == "UPGRADE" ]] && sendToLog "MDM: $macOSSoftwareUpgradeLabelTARGET upgrade is prepared and ready for restart."
[[ "$mdmWorkflowTYPE" == "UPDATE" ]] && sendToLog "MDM: $macOSSoftwareUpdateLABEL update is prepared and ready for restart."
[[ "$mdmWorkflowTYPE" == "INSTALLER" ]] && sendToLog "MDM: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installer is prepared and ready for restart."
if [[ "$mdmWorkflowTYPE" == "INSTALLER" ]] && [[ "$currentUserNAME" != "FALSE" ]]; then
sendToLog "MDM: Forcing logout for user \"$currentUserNAME\" with UID $currentUserUID."
launchctl bootout "user/$currentUserUID" &
disown %
fi
kill -9 "$mdmWorkflowStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmWorkflowDebugPID" > /dev/null 2>&1
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM COMPLETED ****"
installRestartComplete
else # The macOS update/upgrade workflow timed out so clean-up and try again later.
kill -9 "$mdmWorkflowStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmWorkflowDebugPID" > /dev/null 2>&1
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM FAILED ****"
kickSoftwareUpdateD
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToMDMWorkflowLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else # No user authentication MDM failover option.
sendToMDMWorkflowLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
notifyInstallNowFailure
errorExit
fi
else # Default super workflow.
if [[ "$currentUserNAME" != "FALSE" ]] && [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToMDMWorkflowLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, failing over to user authenticated workflow."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else # No user authentication MDM failover option.
deferSECONDS="$errorDeferSECONDS"
sendToMDMWorkflowLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Installation of macOS update/upgrade timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
[[ "$currentUserNAME" != "FALSE" ]] && notifyFailure
makeLaunchDaemonCalendar
fi
fi
fi
else # The MDM push workflow failed so clean-up and try again later.
kill -9 "$mdmCommandStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmCommandDebugPID" > /dev/null 2>&1
sendToMDMCommandLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM PUSH FAILED ****"
sendToMDMCommandLog "Error: Failed to send MDM install update/upgrade request. Verify that the Jamf Pro API account \"$jamfACCOUNT\" has the privileges \"Jamf Pro Server Objects > Computers > Create & Read\" and \"Jamf Pro Server Actions > Send Computer Remote Command to Download and Install macOS Update\"."
kill -9 "$mdmWorkflowStreamPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && kill -9 "$mdmWorkflowDebugPID" > /dev/null 2>&1
sendToMDMWorkflowLog "Error: Failed to send MDM install update/upgrade request. Verify that the Jamf Pro API account \"$jamfACCOUNT\" has the privileges \"Jamf Pro Server Objects > Computers > Create & Read\" and \"Jamf Pro Server Actions > Send Computer Remote Command to Download and Install macOS Update\"."
sendToMDMWorkflowLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE/UPGRADE MACOS MDM PUSH FAILED ****"
sendToLog "Error: Failed to send MDM install update/upgrade request. Verify that the Jamf Pro API account \"$jamfACCOUNT\" has the privileges \"Jamf Pro Server Objects > Computers > Create & Read\" and \"Jamf Pro Server Actions > Send Computer Remote Command to Download and Install macOS Update\"."
kickSoftwareUpdateD
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else # No user authentication MDM failover option.
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
sendToStatus "Inactive Error: Push workflow for macOS update/upgrade via MDM failed, install now workflow can not continue."
notifyInstallNowFailure
errorExit
fi
else # Default super workflow.
if [[ "$currentUserNAME" != "FALSE" ]] && [[ "$userAuthMDMFAILOVER" == "TRUE" ]]; then
sendToMDMCommandLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
updateWORKFLOW="MDMFAIL"
unset policyTRIGGERS
installRestartWorkflowActiveUser
return
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Push workflow for macOS update/upgrade via MDM failed, trying again in $deferSECONDS seconds."
[[ "$currentUserNAME" != "FALSE" ]] && notifyFailure
makeLaunchDaemonCalendar
fi
fi
fi
else # Test mode.
sendToLog "Test Mode: Skipping the macOS MDM update/upgrade workflow."
if [[ "$currentUserNAME" != "FALSE" ]]; then
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the MDM preparation notification..."
sleep "$testModeTimeoutSECONDS"
notifyRestart
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the restart notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
# Reset various items after test macOS update is complete.
restartZeroDay
restartDeferralCounters
fi
}
# Install macOS update/upgrade via macOS installer application, and also save results to $superLOG, $installerLOG, and $superPLIST.
installMacOSAPP() {
sendToLog "startosinstall: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install upgrade workflow, check $installerLOG for more detail."
sendToStatus "Running: Starting $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET install upgrade workflow."
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET START ****"
# Background the startosinstall process and send to $installerLOG.
if [[ "$macOSARCH" == "arm64" ]]; then # Apple Silicon.
"/Applications/Install $macOSInstallerNameTARGET.app/Contents/Resources/startosinstall" --agreetolicense --forcequitapps --user "$installACCOUNT" --stdinpass <<< "$installPASSWORD" >> "$installerLOG" 2>&1 &
else # Intel.
"/Applications/Install $macOSInstallerNameTARGET.app/Contents/Resources/startosinstall" --agreetolicense --forcequitapps >> "$installerLOG" 2>&1 &
fi
startosinstallPID=$!
# Watch $installerLOG while waiting for the startosinstall process to complete.
# Note this while read loop has a timeout based on $initialStartTimeoutSECONDS then changes to $macOSInstallerTimeoutSECONDS.
workflowStartTIMEOUT="TRUE"
workflowStartFAIL="TRUE"
workflowTIMEOUT="TRUE"
workflowTimeoutSECONDS=$initialStartTimeoutSECONDS
workflowPHASE=""
workflowCompletePERCENT=0
workflowPreviousCompletePERCENT=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
while read -t $workflowTimeoutSECONDS -r logLINE ; do
# sendToLog "Debug Mode: Function ${FUNCNAME[0]}: logLINE is:\n$logLINE"
if [[ $(echo "$logLINE" | grep -c 'Preparing to run') -gt 0 ]]; then
sendToLog "startosinstall: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET preparing installation..."
sendToInstallerLog "**** TIMESTAMP ****"
workflowPHASE="PREPARING"
workflowTimeoutSECONDS=$macOSInstallerTimeoutSECONDS
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowTimeoutSECONDS is: $workflowTimeoutSECONDS"
workflowStartTIMEOUT="FALSE"
workflowStartFAIL="FALSE"
elif [[ $(echo "$logLINE" | grep -c 'Preparing:') -gt 0 ]] && [[ "$workflowPHASE" == "PREPARING" ]]; then
workflowCompletePERCENT=$(echo "$logLINE" | sed -e 's/Preparing: //' -e 's/\.[0-9]//' | tr -d '\n' | tr -d '\r')
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: workflowCompletePERCENT is: $workflowCompletePERCENT"
if [[ $workflowCompletePERCENT -ge 99 ]]; then
sendToInstallerLog "**** TIMESTAMP ****"
sendToEchoReplaceLine "$macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installation preparing progress: 100%\n"
workflowTIMEOUT="FALSE"
break
elif [[ $workflowCompletePERCENT -gt $workflowPreviousCompletePERCENT ]]; then
sendToEchoReplaceLine "$macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installation preparing progress: $workflowCompletePERCENT%"
workflowPreviousCompletePERCENT=$workflowCompletePERCENT
fi
elif [[ $(echo "$logLINE" | grep -c -e 'Preparing: 99' -e 'Preparing: 100' -e 'Restarting') -gt 0 ]]; then
sendToInstallerLog "**** TIMESTAMP ****"
sendToEchoReplaceLine "$macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET installation preparing progress: 100%\n"
workflowTIMEOUT="FALSE"
break
fi
done < <(tail -n1 -F "$installerLOG" | tr -u '%' '\n')
# If the startosinstall workflow completed, then prepare for restart.
if [[ "$workflowStartTIMEOUT" == "FALSE" ]] && [[ "$workflowStartFAIL" == "FALSE" ]] && [[ "$workflowTIMEOUT" == "FALSE" ]]; then
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL MACOS COMPLETED ****"
sendToLog "Status: $macOSInstallerNameTARGET $macOSInstallerVersionTARGET-$macOSInstallerBuildTARGET is prepared and ready for restart."
[[ "$currentUserNAME" != "FALSE" ]] && notifyRestart
installRestartComplete
else
sendToInstallerLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL MACOS FAILED ****"
kill -9 "$startosinstallPID" > /dev/null 2>&1
if [[ "$installNowOPTION" == "TRUE" ]]; then
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: macOS installer failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer failed to start after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed, install now workflow can not continue."
sendToLog "Error: macOS installer failed, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer failed, install now workflow can not continue."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToLog "Error: macOS installer timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: macOS installer timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
fi
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
if [[ "$workflowStartTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer failed to start after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
elif [[ "$workflowStartFAIL" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer failed, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer failed, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer failed, trying again in $deferSECONDS seconds."
elif [[ "$workflowTIMEOUT" == "TRUE" ]]; then
sendToInstallerLog "Error: macOS installer timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToLog "Error: macOS installer timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: macOS installer timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
fi
[[ "$currentUserNAME" != "FALSE" ]] && notifyFailure
makeLaunchDaemonCalendar
fi
fi
}
# This is the install workflow for all (non-system) updates when enforced.
installRecommendedWorkflow() {
[[ "$currentUserNAME" != "FALSE" ]] && notifyRecommended
if [[ "$testModeOPTION" != "TRUE" ]]; then
installRecommendedSoftwareUpdates
# Double-check to make sure all updates are complete.
fullCheckREQUIRED="TRUE"
checkSoftwareUpdates
checkMacOSUpgrades
checkMacOSDownloads
# If we had failures, then try again.
if [[ "$softwareUpdateERROR" == "TRUE" ]]; then
sendToLog "Warning: Failed to install all recommended (non-system) updates."
[[ "$softwareUpdateRECOMMENDED" == "TRUE" ]] && installRecommendedSoftwareUpdates
# If it failed twice, then try again later.
if [[ "$softwareUpdateERROR" == "TRUE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Failed to install all recommended (non-system) updates after multiple attempts, install now workflow can not continue."
sendToStatus "Inactive Error: Failed to install all recommended (non-system) updates after multiple attempts, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Warning: Failed to install all recommended (non-system) updates after multiple attempts, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Failed to install all recommended (non-system) updates after multiple attempts, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
else
fullCheckREQUIRED="TRUE"
checkSoftwareUpdates
checkMacOSUpgrades
checkMacOSDownloads
fi
fi
# Log status of recommended update completion.
if [[ "$softwareUpdateRECOMMENDED" == "FALSE" ]]; then
sendToLog "Status: Completed installation of all recommended (non-system) updates."
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
# For computers managed via Jamf Pro, submit inventory.
if [[ "$jamfVERSION" != "FALSE" ]]; then
if [[ "$jamfSERVER" != "FALSE" ]]; then
sendToLog "Status: Submitting updated inventory to Jamf Pro. Use --verbose-mode or check /var/log/jamf.log for more detail..."
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
jamfRESULT=$("$jamfBINARY" recon -verbose 2>&1)
jamfRETURN=$?
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRESULT is:\n$jamfRESULT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRETURN is: $jamfRETURN"
else
"$jamfBINARY" recon > /dev/null 2>&1
fi
else
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Unable to submit inventory to Jamf Pro, install now workflow can not continue."
sendToStatus "Inactive Error: Unable to submit inventory to Jamf Pro, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Unable to submit inventory to Jamf Pro, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Unable to submit inventory to Jamf Pro, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
else # Some software updates did not complete
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Some recommended (non-system) updates did not complete, install now workflow can not continue."
sendToStatus "Inactive Error: Some recommended (non-system) updates did not complete, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Warning: Some recommended (non-system) updates did not complete, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Some recommended (non-system) updates did not complete, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
else # Test mode.
sendToLog "Test Mode: Skipping the install recommended updates workflow."
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the recommended update notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
}
# This is the install and restart workflow when a user is NOT logged in.
installRestartWorkflowNoUser() {
# Check to make sure system has enough available free storage space.
checkAvailableStorage
if [[ "$storageREADY" == "FALSE" ]]; then
deferSECONDS="$errorDeferSECONDS"
sendToLog "Status: Current available storage is at $availableStorageGB GBs which is below the $requiredStorageGB GBs that is required for download, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Current available storage is at $availableStorageGB GBs which is below the $requiredStorageGB GBs that is required for download, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
# Check to make sure system is plugged into AC power or that the current battery level is above $batteryLevelPERCENT.
checkAvailablePower
if [[ "$powerREADY" == "FALSE" ]]; then
deferSECONDS="$errorDeferSECONDS"
sendToLog "Status: Current battery level is at $currentBatteryLEVEL% which is below the minimum required level of $batteryLevelPERCENT%, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Current battery level is at $currentBatteryLEVEL% which is below the minimum required level of $batteryLevelPERCENT%, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
# At this point the system has adequate storage and power to continue macOS update/upgrade installation.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: upgradeWORKFLOW is: $upgradeWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: updateWORKFLOW is: $updateWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: installNowOPTION is: $installNowOPTION"
# If MDM workflow is expected, first check for MDM service and bootstrap token.
if { [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]] && [[ "$upgradeWORKFLOW" == "JAMF" ]]; } || { [[ "$softwareUpdateMACOS" == "TRUE" ]] && [[ "$updateWORKFLOW" == "JAMF" ]]; }; then
checkMDMService
if [[ "$mdmSERVICE" == "FALSE" ]]; then
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Can not use MDM workflow because the MDM service is not available, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Can not use MDM workflow because the MDM service is not available, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
else # MDM service is available.
checkBootstrapToken
if [[ "$bootstrapTOKEN" == "FALSE" ]]; then
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Can not use MDM workflow because this computer's bootstrap token is not escrowed, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Can not use MDM workflow because this computer's bootstrap token is not escrowed, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
# Logic to start the appropriate update/upgrade workflow.
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ "$upgradeWORKFLOW" == "LOCAL" ]]; then
if [[ $macOSVERSION -ge 1203 ]]; then # macOS 12.3 or newer upgrade via softwareupdate.
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSSoftwareUpdate
else # Systems older than macOS 12.3 upgrade via installer.
downloadMacOSInstaller
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSAPP
fi
elif [[ "$upgradeWORKFLOW" == "JAMF" ]]; then
downloadMacOSInstaller
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSMDM
else # Apple Silicon with no valid update/upgrade credentials can not enforce macOS upgrade.
deferSECONDS="$errorDeferSECONDS"
sendToLog "Status: No valid Apple Silicon credentials and no logged in user, trying again in $deferSECONDS seconds."
sendToStatus "Pending: No valid Apple Silicon credentials and no logged in user, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
elif [[ "$softwareUpdateMACOS" == "TRUE" ]]; then # macOS updates are available.
if [[ "$updateWORKFLOW" == "LOCAL" ]]; then
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSSoftwareUpdate
elif [[ "$updateWORKFLOW" == "JAMF" ]]; then
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSMDM
else # Apple Silicon with no valid update/upgrade credentials can not enforce macOS update.
deferSECONDS="$errorDeferSECONDS"
sendToLog "Status: No valid Apple Silicon credentials and no logged in user, trying again in $deferSECONDS seconds."
sendToStatus "Pending: No valid Apple Silicon credentials and no logged in user, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
else # Workflow when there is no macOS updates/upgrade.
[[ -n $policyTRIGGERS ]] && runJamfPolicies
if [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]]; then # If requested, restart without updates.
sendToLog "Forced Restart Mode: Restarting computer..."
installRestartComplete
shutdown -o -r +1 &
disown -a
else # Option to restart without updates is not enabled.
sendToLog "Warning: When no macOS update/upgrade is available you must also specify the --restart-without-updates option to restart automatically."
fi
fi
}
# This is the install and restart workflow when a user is logged in.
installRestartWorkflowActiveUser() {
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: upgradeWORKFLOW is: $upgradeWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: updateWORKFLOW is: $updateWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: installNowOPTION is: $installNowOPTION"
# If MDM workflow is expected, first check for MDM service, bootstrap token, and possibly failover to user authentication workflow.
if { [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]] && [[ "$upgradeWORKFLOW" == "JAMF" ]]; } || { [[ "$softwareUpdateMACOS" == "TRUE" ]] && [[ "$updateWORKFLOW" == "JAMF" ]]; }; then
checkMDMService
if [[ "$mdmSERVICE" == "FALSE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverNOSERVICE" == "TRUE" ]] || { [[ "$installNowOPTION" == "TRUE" ]] && [[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]]; }; then
sendToLog "Warning: MDM service is not available, failing over to user authenticated workflow."
upgradeWORKFLOW="MDMFAIL"
else
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Can not use MDM workflow because the MDM service is not available, install now workflow can not continue."
sendToStatus "Inactive Error: Can not use MDM workflow because the MDM service is not available, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Can not use MDM workflow because the MDM service is not available, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Can not use MDM workflow because the MDM service is not available, trying again in $deferSECONDS seconds."
notifyFailure
makeLaunchDaemonCalendar
fi
fi
else # MDM service is available.
checkBootstrapToken
if [[ "$bootstrapTOKEN" == "FALSE" ]]; then
if [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverBOOTSTRAP" == "TRUE" ]] || { [[ "$installNowOPTION" == "TRUE" ]] && [[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]]; }; then
sendToLog "Warning: Missing or invalid bootstrap token escrow, failing over to user authenticated workflow."
updateWORKFLOW="MDMFAIL"
else
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Can not use MDM workflow because this computer's bootstrap token is not escrowed, install now workflow can not continue."
sendToStatus "Inactive Error: Can not use MDM workflow because this computer's bootstrap token is not escrowed, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Can not use MDM workflow because this computer's bootstrap token is not escrowed, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Can not use MDM workflow because this computer's bootstrap token is not escrowed, trying again in $deferSECONDS seconds."
notifyFailure
makeLaunchDaemonCalendar
fi
fi
fi
fi
fi
# Logic to start the appropriate update/upgrade workflow.
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ "$upgradeWORKFLOW" == "LOCAL" ]] || [[ "$upgradeWORKFLOW" == "USER" ]] || [[ "$upgradeWORKFLOW" == "MDMFAIL" ]]; then
if [[ "$upgradeWORKFLOW" == "MDMFAIL" ]]; then
dialogUserAuth
elif [[ "$upgradeWORKFLOW" == "USER" ]] && [[ "$installNowOPTION" != "TRUE" ]]; then
dialogUserAuth
fi
checkAvailableStorage
[[ "$storageREADY" == "FALSE" ]] && notifyStorage
checkAvailablePower
[[ "$powerREADY" == "FALSE" ]] && notifyPower
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "MDMFAIL" ]]; then # macOS 12.3 or newer can upgrade via softwareupdate, but not when the MDM workflow fails.
notifyRestart
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSSoftwareUpdate
else # Systems older than macOS 12.3 upgrade via installer, or when the MDM workflow fails.
prepareTimeEstimateDISPLAY="15-25"
notifyPrepare
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSAPP
fi
elif [[ "$upgradeWORKFLOW" == "JAMF" ]]; then
checkAvailableStorage
[[ "$storageREADY" == "FALSE" ]] && notifyStorage
checkAvailablePower
[[ "$powerREADY" == "FALSE" ]] && notifyPower
prepareTimeEstimateDISPLAY="25-30"
notifyPrepare
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSMDM
fi
elif [[ "$softwareUpdateMACOS" == "TRUE" ]]; then # macOS updates are available.
if [[ "$updateWORKFLOW" == "LOCAL" ]] || [[ "$updateWORKFLOW" == "USER" ]] || [[ "$updateWORKFLOW" == "MDMFAIL" ]]; then
if [[ "$updateWORKFLOW" == "MDMFAIL" ]]; then
dialogUserAuth
elif [[ "$updateWORKFLOW" == "USER" ]] && [[ "$installNowOPTION" != "TRUE" ]]; then
dialogUserAuth
fi
checkAvailableStorage
[[ "$storageREADY" == "FALSE" ]] && notifyStorage
checkAvailablePower
[[ "$powerREADY" == "FALSE" ]] && notifyPower
notifyRestart
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSSoftwareUpdate
elif [[ "$updateWORKFLOW" == "JAMF" ]]; then
checkAvailableStorage
[[ "$storageREADY" == "FALSE" ]] && notifyStorage
checkAvailablePower
[[ "$powerREADY" == "FALSE" ]] && notifyPower
prepareTimeEstimateDISPLAY="5"
notifyPrepare
[[ -n $policyTRIGGERS ]] && runJamfPolicies
installMacOSMDM
fi
else # Workflow when there is no macOS updates/upgrade.
notifyRestart
[[ -n $policyTRIGGERS ]] && runJamfPolicies
if [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]]; then # If requested, restart without updates.
if [[ "$testModeOPTION" != "TRUE" ]]; then
sendToLog "Forced Restart Mode: Restarting computer..."
installRestartComplete
shutdown -o -r +1 &
disown -a
else # Test mode.
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the restart notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
else # Option to restart without updates is not enabled.
sendToLog "Warning: When no macOS update/upgrade is available you must also specify the --restart-without-updates option to restart automatically."
fi
fi
}
# This is the final set of maintenance commands that run after a successful macOS update/upgrade workflow.
installRestartComplete() {
unset recheckDeferSECONDS
restartVALIDATE="TRUE"
touch "$restartValidateFilePATH"
}
# MARK: *** LaunchDaemons ***
################################################################################
# This unloads and deletes any previous LaunchDaemons.
removeLaunchDaemon() {
if [[ -f "/Library/LaunchDaemons/$launchDaemonNAME.plist" ]]; then
sendToLog "Status: Removing previous LaunchDaemon $launchDaemonNAME.plist."
launchctl bootout system "/Library/LaunchDaemons/$launchDaemonNAME.plist" 2> /dev/null
rm -f "/Library/LaunchDaemons/$launchDaemonNAME.plist"
fi
defaults delete "$superPLIST" FailSafeActive 2> /dev/null
}
# Create a LaunchDaemon to run super-starter again right now, thus releasing any Jamf Pro Policy that may have started super.
makeLaunchDaemonRestartNow() {
removeLaunchDaemon
# This creates a LaunchDaemon.plist file.
/bin/cat < "/Library/LaunchDaemons/$launchDaemonNAME.plist"
Label
$launchDaemonNAME
LaunchOnlyOnce
AbandonProcessGroup
UserName
root
ProgramArguments
$superFOLDER/super-starter
RunAtLoad
EOLDL
# Set proper permissions and load the LaunchDaemon.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: LaunchDaemon $launchDaemonNAME.plist:\n$(cat "/Library/LaunchDaemons/$launchDaemonNAME.plist")"
chmod 644 "/Library/LaunchDaemons/$launchDaemonNAME.plist"
chown root:wheel "/Library/LaunchDaemons/$launchDaemonNAME.plist"
sendToLog "Exit: LaunchDaemon $launchDaemonNAME.plist is scheduled to start right now."
sendToPending "Right Now."
launchctl bootstrap system "/Library/LaunchDaemons/$launchDaemonNAME.plist"
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION EXIT ****"
rm -f "$superPIDFILE"
exit 0
}
# Create a LaunchDaemon to run super-starter again after restart.
makeLaunchDaemonOnStartup() {
removeLaunchDaemon
# This creates a LaunchDaemon.plist file.
/bin/cat < "/Library/LaunchDaemons/$launchDaemonNAME.plist"
Label
$launchDaemonNAME
LaunchOnlyOnce
AbandonProcessGroup
UserName
root
ProgramArguments
$superFOLDER/super-starter
RunAtLoad
EOLDL
# Set proper permissions for the LaunchDaemon.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: LaunchDaemon $launchDaemonNAME.plist:\n$(cat "/Library/LaunchDaemons/$launchDaemonNAME.plist")"
chmod 644 "/Library/LaunchDaemons/$launchDaemonNAME.plist"
chown root:wheel "/Library/LaunchDaemons/$launchDaemonNAME.plist"
sendToLog "Status: LaunchDaemon $launchDaemonNAME.plist is scheduled at next startup."
}
# Create a LaunchDaemon to run super-starter again $deferSECONDS from now.
makeLaunchDaemonCalendar() {
removeLaunchDaemon
# Calculate the appropriate deferment timer for the LaunchDaemon.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: deferSECONDS is: $deferSECONDS"
deferCALC=$(($(date +%s) + deferSECONDS))
month=$(date -j -f "%s" "$deferCALC" "+%m" | xargs)
day=$(date -j -f "%s" "$deferCALC" "+%e" | xargs)
hour=$(date -j -f "%s" "$deferCALC" "+%H" | xargs)
minute=$(date -j -f "%s" "$deferCALC" "+%M" | xargs)
# This creates a LaunchDaemon.plist file.
/bin/cat < "/Library/LaunchDaemons/$launchDaemonNAME.plist"
Label
$launchDaemonNAME
LaunchOnlyOnce
AbandonProcessGroup
UserName
root
ProgramArguments
$superFOLDER/super-starter
StartCalendarInterval
Month
$month
Day
$day
Hour
$hour
Minute
$minute
EOLDL
# Set proper permissions and load the LaunchDaemon.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: LaunchDaemon $launchDaemonNAME.plist:\n$(cat "/Library/LaunchDaemons/$launchDaemonNAME.plist")"
chmod 644 "/Library/LaunchDaemons/$launchDaemonNAME.plist"
chown root:wheel "/Library/LaunchDaemons/$launchDaemonNAME.plist"
sendToLog "Exit: LaunchDaemon $launchDaemonNAME.plist is scheduled to start at $hour:$minute on $month/$day."
sendToPending "$(date -j -f "%s" "$deferCALC" "+%F %T" | xargs)"
launchctl bootstrap system "/Library/LaunchDaemons/$launchDaemonNAME.plist"
cleanExit
}
# MARK: *** User Interface Prep ***
################################################################################
# Set language strings for notifications and dialogs.
setDisplayLanguage() {
#### Langauge for the restart button in dialogs. Note that for deadline dialogs this is the default button.
restartButtonDISPLAY="Restart"
#### Language for the deferral button in dialogs that do not show the $menuDeferOPTION but instead show the $DefaultDefer time in the button display. Note that for non-deadline dialogs this is the default button.
deferForButtonDISPLAY="Defer For"
#### Language for the deferral button in dialogs that also show the $menuDeferOPTION. Note that for non-deadline dialogs this is the default button.
deferButtonDISPLAY="Defer"
#### Language for the ok button in certain notifications.
okButtonDISPLAY="OK"
### Language for various deferral time durations.
dialogRestartOrDeferMinutesDISPLAY="Minutes"
dialogRestartOrDeferHourDISPLAY="Hour"
dialogRestartOrDeferHoursDISPLAY="Hours"
dialogRestartOrDeferDayDISPLAY="Day"
#### This code generates the $titleWorkflowDISPLAY variable to include the appropriate macOS update or upgrade.
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then # Newer systems upgrade via softwareupdate unless via MDM workflow.
titleWorkflowDISPLAY="macOS $macOSSoftwareUpgradeVersionTARGET Upgrade"
else # Systems older than macOS 12.3 or using MDM workflow upgrade via installer.
titleWorkflowDISPLAY="macOS $macOSInstallerVersionTARGET Upgrade"
fi
elif [[ "$softwareUpdateMACOS" == "TRUE" ]]; then # macOS updates are available.
titleWorkflowDISPLAY="macOS $macOSSoftwareUpdateVERSION Update"
else # Only recommended (non-system) updates are available.
titleWorkflowDISPLAY="Apple Software Updates"
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: titleWorkflowDISPLAY is: $titleWorkflowDISPLAY"
#### Useful display variables:
# $titleWorkflowDISPLAY is the macOS update or upgrade workflow title that includes the target macOS version number.
# $availableStorageGB is the number of gigabytes of currently available storage.
# $requiredStorageGB is the number of gigabytes required for the current macOS update/upgrade workflow.
# $countDISPLAY is the current number of user soft/hard deferrals.
# $countMaxDISPLAY is the maximum number of user soft/hard deferrals.
# $softDaysMAX is the maximum number of deferral days before a soft deadline.
# $hardDaysMAX is the maximum number of deferral days before a hard deadline.
# $zeroDayDISPLAY is the date:time of the zero day that is used for calculating the maximum days deferral deadlines.
# $deadlineDaysDISPLAY is the soonest date:time based on evaluating the maximum days deferral deadlines.
# $deadlineDateDISPLAY is the soonest date:time based on evaluating the maximum date deferral deadlines.
# $deadlineDISPLAY is the soonest date:time based on evaluating both the maximum date and days deferral deadlines.
# $prepareTimeEstimateDISPLAY is a estimated number of minutes that an update/upgrade process needs for preparation before a restart.
# $currentUserRealNAME is the current users full (display friendly) name.
# See $dateFORMAT and $timeFORMAT in the setDefaults() function to adjust how the date:time is shown.
#### Language for notifyInstallNowStart(), a non-interactive notification informing the user that the install now workflow has started.
notifyInstallNowStartTITLE="Apple Software Update Starting"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyInstallNowStartBodyIBM="The Apple software update workflow has started, and you will be notified if this computer needs to restart.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the Apple software update process."
notifyInstallNowStartBodyJAMF="The Apple software update workflow has started, and you will be notified if this computer needs to restart.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the Apple software update process."
#### Language for notifyInstallNowDownload(), a non-interactive notification informing the user that the install now workflow is downloading the macOS update/upgrade.
notifyInstallNowDownloadTITLE="Downloading $titleWorkflowDISPLAY"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyInstallNowDownloadBodyIBM="The $titleWorkflowDISPLAY is downloading from Apple. This may take a while, but you will be notified before this computer automatically restarts.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update/upgrade process."
notifyInstallNowDownloadBodyJAMF="The $titleWorkflowDISPLAY is downloading from Apple. This may take a while, but you will be notified before this computer automatically restarts.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update/upgrade process."
#### Language for notifyInstallNowUpToDate(), a non-interactive notification informing the user that the install now workflow doesn't have anything else to do.
notifyInstallNowUpToDateTITLE="Apple Software Update Complete"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyInstallNowUpToDateBodyIBM="Apple system software is already up to date or is the latest version allowed by management."
notifyInstallNowUpToDateBodyJAMF="Apple system software is already up to date or is the latest version allowed by management."
#### Language for notifyInstallNowFailure(), a non-interactive notification informing the user that the install now workflow has failed.
# This is used for all update/upgrade workflows if they fail to start or timeout after a pending restart notification has been shown.
notifyInstallNowFailureTITLE="$titleWorkflowDISPLAY Failed"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyInstallNowFailureBodyIBM="The $titleWorkflowDISPLAY failed to complete, the computer will not be restarting.\n\nYou can try again or consider contacting your technical support team if you're experiencing consistent failures."
notifyInstallNowFailureBodyJAMF="The $titleWorkflowDISPLAY failed to complete, the computer will not be restarting.
You can try again or consider contacting your technical support team if you're experiencing consistent failures."
#### Language for notifyStorage(), a non-interactive notification informing the user that there is insufficient free space for macOS update/upgrade.
# This is used for both non-deadline and deadline workflows.
notifyStorageTITLE="Insufficient Storage For $titleWorkflowDISPLAY"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyStorageBodyDefaultIBM="A required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.\n\nPlease use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDefaultJAMF="A required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.
Please use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDeadlineCountIBM="You have deferred the maximum number of $countMaxDISPLAY times.\n\nA required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.\n\nPlease use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDeadlineCountJAMF="You have deferred the maximum number of $countMaxDISPLAY times.
A required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.
Please use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDeadlineDaysIBM="You have deferred the maximum number of $hardDaysMAX days.\n\nA required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.\n\nPlease use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDeadlineDaysJAMF="You have deferred the maximum number of $hardDaysMAX days.
A required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available. Please use the Storage settings to remove unnecessary items."
notifyStorageBodyDeadlineDateIBM="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nA required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.\n\nPlease use the storage settings (shown behind this notification) to remove unnecessary items."
notifyStorageBodyDeadlineDateJAMF="The deferment deadline of $deadlineDateDISPLAY has passed.
A required macOS update needs $requiredStorageGB GBs of free storage space and only $availableStorageGB GBs is available.
Please use the storage settings (shown behind this notification) to remove unnecessary items."
#### Language for notifyPower(), a non-interactive notification informing the user that they need to plug the computer into AC power.
# This is used for both non-deadline and deadline workflows.
notifyPowerTITLE="$titleWorkflowDISPLAY Requires Power Source"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyPowerBodyDefaultIBM="You must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDefaultJAMF="You must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineCountIBM="You have deferred the maximum number of $countMaxDISPLAY times.\n\nYou must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineCountJAMF="You have deferred the maximum number of $countMaxDISPLAY times.
You must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineDaysIBM="You have deferred the maximum number of $hardDaysMAX days.\n\nYou must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineDaysJAMF="You have deferred the maximum number of $hardDaysMAX days.
You must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineDateIBM="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nYou must connect this computer to a power supply in order to install the required macOS update."
notifyPowerBodyDeadlineDateJAMF="The deferment deadline of $deadlineDateDISPLAY has passed.
You must connect this computer to a power supply in order to install the required macOS update."
#### Language for notifyPrepare(), a non-interactive notification informing the user that a update/upgrade preparation process has started.
# This is used for both non-deadline and deadline workflows.
notifyPrepTITLE="$titleWorkflowDISPLAY Requires Restart"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyPrepBodyDefaultIBM="A required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDefaultJAMF="A required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineCountIBM="You have deferred the maximum number of $countMaxDISPLAY times.\n\nA required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineCountJAMF="You have deferred the maximum number of $countMaxDISPLAY times.
A required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineDaysIBM="You have deferred the maximum number of $hardDaysMAX days.\n\nA required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineDaysJAMF="You have deferred the maximum number of $hardDaysMAX days.
A required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineDateIBM="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nA required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.\n\nDuring this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
notifyPrepBodyDeadlineDateJAMF="The deferment deadline of $deadlineDateDISPLAY has passed.
A required software update will automatically restart this computer in about $prepareTimeEstimateDISPLAY minutes.
During this time you can continue to use the computer or lock the screen, but please do not restart or sleep the computer as it will prolong the update process."
#### Language for notifyRestart(), a non-interactive notification informing the user that the computer is going to restart very soon.
# This is used for all softwareupdate workflows and near the end of the MDM workflow.
# This is used for both non-deadline and deadline workflows.
notifyRestartTITLE="$titleWorkflowDISPLAY Requires Restart"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyRestartBodyDefaultIBM="This computer will automatically restart very soon.\n\nSave any open documents now."
notifyRestartBodyDefaultJAMF="This computer will automatically restart very soon.
Save any open documents now."
notifyRestartBodyDeadlineCountIBM="You have deferred the maximum number of $countMaxDISPLAY times.\n\nThis computer will automatically restart very soon.\n\nSave any open documents now."
notifyRestartBodyDeadlineCountJAMF="You have deferred the maximum number of $countMaxDISPLAY times.
This computer will automatically restart very soon.
Save any open documents now."
notifyRestartBodyDeadlineDaysIBM="You have deferred the maximum number of $hardDaysMAX days.\n\nThis computer will automatically restart very soon.\n\nSave any open documents now."
notifyRestartBodyDeadlineDaysJAMF="You have deferred the maximum number of $hardDaysMAX days.
This computer will automatically restart very soon.
Save any open documents now."
notifyRestartBodyDeadlineDateIBM="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nThis computer will automatically restart very soon.\n\nSave any open documents now."
notifyRestartBodyDeadlineDateJAMF="The deferment deadline of $deadlineDateDISPLAY has passed.
This computer will automatically restart very soon.
Save any open documents now."
#### Language for notifyRecommended(), a non-interactive notification informing the user that the install now workflow is installing recommended (non-system) software updates.
notifyRecommendedTITLE="Installing $titleWorkflowDISPLAY"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyRecommendedBodyIBM="Required Apple software updates are now installing.\n\nThe computer will not restart but some Apple applications (like Safari) may automatically restart.\n\nThis should only take a few minutes, but please do not restart or sleep the computer as it will prolong the update process."
notifyRecommendedBodyJAMF="Required Apple software updates are now installing.
The computer will not restart but some Apple applications (like Safari) may automatically restart.
This should only take a few minutes, but please do not restart or sleep the computer as it will prolong the update process."
#### Language for notifyFailure(), a non-interactive notification informing the user that the managed update/upgrade process has failed.
# This is used for all update/upgrade workflows if they fail to start or timeout after a pending restart notification has been shown.
notifyFailureTITLE="$titleWorkflowDISPLAY Failed"
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
notifyFailureBodyIBM="The $titleWorkflowDISPLAY failed to complete.\n\nThe computer will not restart right now, but you will be notified later when the software update is attempted again."
notifyFailureBodyJAMF="The $titleWorkflowDISPLAY failed to complete.
The computer will not restart right now, but you will be notified later when the software update is attempted again."
#### Language for dialogRestartOrDefer(), an interactive dialog giving the user a choice to defer the update or restart.
dialogRestartOrDeferTITLE="$titleWorkflowDISPLAY Requires Restart"
dialogRestartOrDeferDeferMenuTitleIBM="Defer software update for:" # jamfHelper does not allow for customizing the deferral menu language.
dialogRestartOrDeferTimeoutIBM="Please make selection in" # jamfHelper does not allow for customizing the display timeout language.
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
dialogRestartOrDeferBodyUnlimitedIBM="• No deadline date and unlimited deferrals.\n"
dialogRestartOrDeferBodyUnlimitedJAMF="• No deadline date and unlimited deferrals."
dialogRestartOrDeferBodyDateIBM="• Deferral available until $deadlineDISPLAY.\n"
dialogRestartOrDeferBodyDateJAMF="• Deferral available until $deadlineDISPLAY."
dialogRestartOrDeferBodyCountIBM="• $countDISPLAY out of $countMaxDISPLAY deferrals remaining.\n"
dialogRestartOrDeferBodyCountJAMF="• $countDISPLAY out of $countMaxDISPLAY deferrals remaining."
dialogRestartOrDeferBodyDateCountIBM="• Deferral available until $deadlineDISPLAY.\n\n• $countDISPLAY out of $countMaxDISPLAY deferrals remaining.\n"
dialogRestartOrDeferBodyDateCountJAMF="• Deferral available until $deadlineDISPLAY.
• $countDISPLAY out of $countMaxDISPLAY deferrals remaining."
#### Language for dialogSoftDeadline(), an interactive dialog when a soft deadline has passed, giving the user only one button to continue the workflow.
dialogSoftDeadlineTITLE="$titleWorkflowDISPLAY Requires Restart"
dialogSoftDeadlineTimeoutIBM="Update will automatically start in" # jamfHelper does not allow for customizing the display timeout language.
# Note that IBM Notifier interprets "\n" as a return, while jamfHelper interprets "real" returns.
dialogSoftDeadlineBodyCountIBM="You have deferred the maximum number of $countMaxDISPLAY times."
dialogSoftDeadlineBodyCountJAMF="You have deferred the maximum number of $countMaxDISPLAY times."
dialogSoftDeadlineBodyDaysIBM="You have deferred the maximum number of $softDaysMAX days."
dialogSoftDeadlineBodyDaysJAMF="You have deferred the maximum number of $softDaysMAX days."
dialogSoftDeadlineBodyDateIBM="The deferment deadline has passed:\n$deadlineDateDISPLAY."
dialogSoftDeadlineBodyDateJAMF="The deferment deadline has passed:
$deadlineDateDISPLAY."
#### Language for dialogUserAuth(), an interactive IBM Notifier dialog to collect user credentials for macOS update/upgrade workflow
# This is used for both non-deadline and deadline workflows.
dialogUserAuthTITLE="$titleWorkflowDISPLAY Requires Authentication"
dialogUserAuthDefaultBODY="You must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthDeadlineCountBODY="You have deferred the maximum number of $countMaxDISPLAY times.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthDeadlineSoftDaysBODY="You have deferred the maximum number of $softDaysMAX days.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthDeadlineHardDaysBODY="You have deferred the maximum number of $hardDaysMAX days.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthDeadlineDateBODY="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthPassTITLE="Enter Password Here:"
dialogUserAuthPassPLACEHOLDER="Password Required"
dialogUserAuthRetryDefaultBODY="You must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthRetryDeadlineCountBODY="You have deferred the maximum number of $countMaxDISPLAY times.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthRetryDeadlineSoftDaysBODY="You have deferred the maximum number of $softDaysMAX days.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthRetryDeadlineHardDaysBODY="You have deferred the maximum number of $hardDaysMAX days.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthRetryDeadlineDateBODY="The deferment deadline of $deadlineDateDISPLAY has passed.\n\nYou must enter the password for user \"$currentUserRealNAME\" to install the $titleWorkflowDISPLAY.\n"
dialogUserAuthRetryPassTITLE="Authentication Failed - Try Password Again:"
dialogUserAuthRetryPassPLACEHOLDER="Password Required"
}
# For IBM Notifier dialogs set a new $deferButtonDISPLAY showing the $deferSECONDS.
setDeferButton() {
if [[ $deferSECONDS -lt 3600 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY $((deferSECONDS / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ $deferSECONDS -eq 3600 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY 1 $dialogRestartOrDeferHourDISPLAY"
elif [[ $deferSECONDS -gt 3600 ]] && [[ $deferSECONDS -lt 7200 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY 1 $dialogRestartOrDeferHourDISPLAY $((deferSECONDS % 3600 / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ $deferSECONDS -ge 7200 ]] && [[ $deferSECONDS -lt 86400 ]] && [[ $((deferSECONDS % 3600)) -eq 0 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY $((deferSECONDS / 3600)) $dialogRestartOrDeferHoursDISPLAY"
elif [[ $deferSECONDS -gt 7200 ]] && [[ $deferSECONDS -lt 86400 ]] && [[ $((deferSECONDS % 3600)) -ne 0 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY $((deferSECONDS / 3600)) $dialogRestartOrDeferHoursDISPLAY $((deferSECONDS % 3600 / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ $deferSECONDS -eq 86400 ]]; then
deferButtonDISPLAY="$deferForButtonDISPLAY 1 $dialogRestartOrDeferDayDISPLAY"
fi
}
# For IBM Notifier dialogs handle the $helpBUTTON, $warningBUTTON, and $displaySilentlyOPTION options.
setIbmNotifierOptions() {
if [[ -n $helpBUTTON ]]; then
if [[ $(echo "$helpBUTTON" | grep -c '^http://\|^https://\|^mailto:\|^jamfselfservice://') -gt 0 ]]; then
if [[ $(echo "$helpButtonOPTION" | grep -c '^http://\|^https://') -gt 0 ]]; then
commandRESULT=$(curl -Is "$helpButtonOPTION" | head -1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is: $commandRESULT"
if [[ $(echo "$commandRESULT" | grep -c '200') -gt 0 ]] || [[ $(echo "$commandRESULT" | grep -c '302') -gt 0 ]]; then
ibmNotifierARRAY+=(-help_button_cta_type link -help_button_cta_payload "$helpBUTTON")
else
sendToLog "Warning: Help button not shown because URL cannot be reached: $helpBUTTON"
fi
else
ibmNotifierARRAY+=(-help_button_cta_type link -help_button_cta_payload "$helpBUTTON")
fi
else
ibmNotifierARRAY+=(-help_button_cta_type infopopup -help_button_cta_payload "$helpBUTTON")
fi
fi
if [[ -n $warningBUTTON ]]; then
if [[ $(echo "$warningBUTTON" | grep -c '^http://\|^https://\|^mailto:\|^jamfselfservice://') -gt 0 ]]; then
if [[ $(echo "$warningButtonOPTION" | grep -c '^http://\|^https://') -gt 0 ]]; then
commandRESULT=$(curl -Is "$warningButtonOPTION" | head -1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is: $commandRESULT"
if [[ $(echo "$commandRESULT" | grep -c '200') -gt 0 ]] || [[ $(echo "$commandRESULT" | grep -c '302') -gt 0 ]]; then
ibmNotifierARRAY+=(-warning_button_cta_type link -warning_button_cta_payload "$warningBUTTON")
else
sendToLog "Warning: Warning button not shown because URL cannot be reached: $warningBUTTON"
fi
else
ibmNotifierARRAY+=(-warning_button_cta_type link -warning_button_cta_payload "$warningBUTTON")
fi
else
ibmNotifierARRAY+=(-warning_button_cta_type infopopup -warning_button_cta_payload "$warningBUTTON")
fi
fi
[[ "$displaySilentlyOPTION" == "TRUE" ]] && ibmNotifierARRAY+=(-silent)
}
# Open $ibmNotifierBINARY for non-interactive notifications using the $ibmNotifierARRAY[] options.
openNotifyIbmNotifier() {
# Kill any previous notifications so new ones can take its place.
kill -9 "$notifyPID" > /dev/null 2>&1
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
# Open notification in the background allowing super to continue.
setIbmNotifierOptions
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
"$ibmNotifierBINARY" "${ibmNotifierARRAY[@]}" &
disown %
notifyPID=$!
}
# Open $jamfHELPER for non-interactive notifications using the $jamfHelperARRAY[] options.
openNotifyJamfHelper() {
# Kill any previous notifications so new ones can take its place.
kill -9 "$notifyPID" > /dev/null 2>&1
killall -9 "jamfHelper" > /dev/null 2>&1
# Open notification in the background allowing super to continue.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
"$jamfHELPER" "${jamfHelperARRAY[@]}" &
disown %
notifyPID=$!
}
# Open $ibmNotifierBINARY for interactive dialogs using the $ibmNotifierARRAY[] options, and also handle any $displayTimeoutSECONDS, $displayRedrawSECONDS, $secureAccessoryPAYLOAD, $displayAccessoryCONTENT, and $displayAccessoryPAYLOAD options.
openDialogIbmNotifier() {
# Kill any previous notifications so new ones can take its place.
kill -9 "$notifyPID" > /dev/null 2>&1
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
unset dialogRESULT
unset dialogRETURN
customDisplayACCESSORY="FALSE"
if [[ -n $displayAccessoryCONTENT ]]; then
if [[ $(echo "$displayAccessoryCONTENT" | grep -c '^http://\|^https://') -gt 0 ]]; then
if [[ $displayAccessoryTYPE =~ ^TEXTBOX$|^HTMLBOX$|^HTML$ ]]; then
displayAccessoryPAYLOAD=$(curl -s -f "$displayAccessoryCONTENT")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryPAYLOAD is:\n$displayAccessoryPAYLOAD"
if [[ -n $displayAccessoryPAYLOAD ]]; then
customDisplayACCESSORY="TRUE"
else
sendToLog "Warning: Custom display accessory not shown because URL cannot be downloaded: $displayAccessoryCONTENT"
fi
else # $displayAccessoryTYPE is IMAGE or VIDEO or VIDEOAUTO.
commandRESULT=$(curl -Is "$displayAccessoryCONTENT" | head -1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is: $commandRESULT"
if [[ $(echo "$commandRESULT" | grep -c '200') -gt 0 ]] || [[ $(echo "$commandRESULT" | grep -c '302') -gt 0 ]]; then
displayAccessoryPAYLOAD="$displayAccessoryCONTENT"
customDisplayACCESSORY="TRUE"
else
sendToLog "Warning: Custom display accessory not shown because URL cannot be found: $displayAccessoryCONTENT"
fi
fi
else # Assume it's a local path.
if [[ $displayAccessoryTYPE =~ ^TEXTBOX$|^HTMLBOX$|^HTML$ ]]; then
displayAccessoryPAYLOAD=$(<"$displayAccessoryCONTENT")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: displayAccessoryPAYLOAD is:\n$displayAccessoryPAYLOAD"
if [[ -n $displayAccessoryPAYLOAD ]]; then
customDisplayACCESSORY="TRUE"
else
sendToLog "Warning: Custom display accessory not shown because file path cannot be read: $displayAccessoryCONTENT"
fi
else # $displayAccessoryTYPE is IMAGE or VIDEO or VIDEOAUTO.
if [[ -f "$displayAccessoryCONTENT" ]]; then
displayAccessoryPAYLOAD="$displayAccessoryCONTENT"
customDisplayACCESSORY="TRUE"
else
sendToLog "Warning: Custom display accessory not shown because file path cannot be found: $displayAccessoryCONTENT"
fi
fi
fi
fi
if [[ "$customDisplayACCESSORY" == "TRUE" ]]; then
[[ "$displayAccessoryTYPE" == "TEXTBOX" ]] && ibmNotifierARRAY+=(-accessory_view_type whitebox -accessory_view_payload "$displayAccessoryPAYLOAD")
[[ "$displayAccessoryTYPE" == "HTMLBOX" ]] && ibmNotifierARRAY+=(-accessory_view_type htmlwhitebox -accessory_view_payload "$displayAccessoryPAYLOAD")
[[ "$displayAccessoryTYPE" == "HTML" ]] && ibmNotifierARRAY+=(-accessory_view_type html -accessory_view_payload "$displayAccessoryPAYLOAD")
[[ "$displayAccessoryTYPE" == "IMAGE" ]] && ibmNotifierARRAY+=(-accessory_view_type image -accessory_view_payload "$displayAccessoryPAYLOAD")
[[ "$displayAccessoryTYPE" == "VIDEO" ]] && ibmNotifierARRAY+=(-accessory_view_type video -accessory_view_payload "/url $displayAccessoryPAYLOAD")
[[ "$displayAccessoryTYPE" == "VIDEOAUTO" ]] && ibmNotifierARRAY+=(-accessory_view_type video -accessory_view_payload "/url $displayAccessoryPAYLOAD /autoplay")
fi
setIbmNotifierOptions
if [[ -n $displayRedrawSECONDS ]]; then
[[ -n $displayTimeoutSECONDS ]] && displayTimeoutSECONDS=$((displayTimeoutSECONDS-1))
ibmNotifierBackupARRAY=("${ibmNotifierARRAY[@]}")
while [[ -z $dialogRETURN ]] || [[ "$dialogRETURN" -eq 137 ]]; do
if [[ "$customDisplayACCESSORY" == "TRUE" ]]; then
if [[ -n $secureAccessoryPAYLOAD ]]; then
ibmNotifierARRAY+=(-secondary_accessory_view_type secureinput -secondary_accessory_view_payload "$secureAccessoryPAYLOAD" -timeout "$displayTimeoutSECONDS")
else
{ [[ -n $menuDeferSECONDS ]] && [[ -z $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-secondary_accessory_view_type dropdown -secondary_accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0")
{ [[ -z $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-secondary_accessory_view_type timer -secondary_accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
if [[ -n $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; then
sendToLog "Warning: Unable to show display timeout countdown due to the custom display accessory option. However, there is still a display timeout at $displayTimeoutSECONDS seconds."
ibmNotifierARRAY+=(-secondary_accessory_view_type dropdown -secondary_accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0" -timeout "$displayTimeoutSECONDS")
fi
fi
else # No custom display accessory.
if [[ -n $secureAccessoryPAYLOAD ]]; then
ibmNotifierARRAY+=(-accessory_view_type secureinput -accessory_view_payload "$secureAccessoryPAYLOAD" -timeout "$displayTimeoutSECONDS")
else
{ [[ -n $menuDeferSECONDS ]] && [[ -z $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type dropdown -accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0")
{ [[ -z $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type timer -accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
{ [[ -n $displayTimeoutSECONDS ]] && [[ -n $menuDeferSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type dropdown -accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0" -secondary_accessory_view_type timer -secondary_accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMODE is: $userAuthMODE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
(sleep "$displayRedrawSECONDS"; killall -9 "IBM Notifier" "IBM Notifier Popup") &
killerPID=$!
disown
dialogRESULT=$("$ibmNotifierBINARY" "${ibmNotifierARRAY[@]}")
dialogRETURN="$?"
kill -0 "$killerPID" && kill -9 "$killerPID" > /dev/null 2>&1
{ [[ "$verboseModeOPTION" == "TRUE" ]] && [[ "$userAuthMODE" != "TRUE" ]]; } && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
{ [[ "$verboseModeOPTION" == "TRUE" ]] && [[ "$userAuthMODE" == "TRUE" ]]; } && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRETURN is: $dialogRETURN"
[[ -n $displayTimeoutSECONDS ]] && displayTimeoutSECONDS=$((displayTimeoutSECONDS - displayRedrawSECONDS))
ibmNotifierARRAY=("${ibmNotifierBackupARRAY[@]}")
done
else
if [[ "$customDisplayACCESSORY" == "TRUE" ]]; then
if [[ -n $secureAccessoryPAYLOAD ]]; then
ibmNotifierARRAY+=(-secondary_accessory_view_type secureinput -secondary_accessory_view_payload "$secureAccessoryPAYLOAD" -timeout "$displayTimeoutSECONDS")
else
{ [[ -n $menuDeferSECONDS ]] && [[ -z $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-secondary_accessory_view_type dropdown -secondary_accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0")
{ [[ -z $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-secondary_accessory_view_type timer -secondary_accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
if [[ -n $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; then
sendToLog "Warning: Unable to show display timeout countdown due to the custom display accessory option. However, there is still a display timeout at $displayTimeoutSECONDS seconds."
ibmNotifierARRAY+=(-secondary_accessory_view_type dropdown -secondary_accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0" -timeout "$displayTimeoutSECONDS")
fi
fi
else # No custom display accessory.
if [[ -n $secureAccessoryPAYLOAD ]]; then
ibmNotifierARRAY+=(-accessory_view_type secureinput -accessory_view_payload "$secureAccessoryPAYLOAD" -timeout "$displayTimeoutSECONDS")
else
{ [[ -n $menuDeferSECONDS ]] && [[ -z $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type dropdown -accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0")
{ [[ -z $menuDeferSECONDS ]] && [[ -n $displayTimeoutSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type timer -accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
{ [[ -n $displayTimeoutSECONDS ]] && [[ -n $menuDeferSECONDS ]]; } && ibmNotifierARRAY+=(-accessory_view_type dropdown -accessory_view_payload "/title $dialogRestartOrDeferDeferMenuTitleIBM /list $menuDisplayTEXT /selected 0" -secondary_accessory_view_type timer -secondary_accessory_view_payload "$displayTimeoutTEXT %@" -timeout "$displayTimeoutSECONDS")
fi
fi
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: userAuthMODE is: $userAuthMODE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
dialogRESULT=$("$ibmNotifierBINARY" "${ibmNotifierARRAY[@]}")
dialogRETURN="$?"
{ [[ "$verboseModeOPTION" == "TRUE" ]] && [[ "$userAuthMODE" != "TRUE" ]]; } && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
{ [[ "$verboseModeOPTION" == "TRUE" ]] && [[ "$userAuthMODE" == "TRUE" ]]; } && sendToEcho "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRETURN is: $dialogRETURN"
fi
}
# Open $jamfHELPER for interactive dialogs using the $jamfHelperARRAY[] options, and also handle any $displayTimeoutSECONDS and $displayRedrawSECONDS options.
openDialogJamfHelper() {
# Kill any previous notifications so new ones can take its place.
kill -9 "$notifyPID" > /dev/null 2>&1
killall -9 "jamfHelper" > /dev/null 2>&1
unset dialogRESULT
unset dialogRETURN
if [[ -n $displayRedrawSECONDS ]]; then
[[ -n $displayTimeoutSECONDS ]] && displayTimeoutSECONDS=$((displayTimeoutSECONDS-1))
jamfHelperBackupARRAY=("${jamfHelperARRAY[@]}")
while [[ -z $dialogRESULT ]]; do
[[ -n $displayTimeoutSECONDS ]] && jamfHelperARRAY+=(-timeout "$displayTimeoutSECONDS" -countdown)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
(sleep "$displayRedrawSECONDS"; killall -9 "jamfHelper") &
killerPID=$!
disown
dialogRESULT=$("$jamfHELPER" "${jamfHelperARRAY[@]}")
dialogRETURN="$?"
kill -0 "$killerPID" && kill -9 "$killerPID" > /dev/null 2>&1
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRETURN is: $dialogRETURN"
[[ -n $displayTimeoutSECONDS ]] && displayTimeoutSECONDS=$((displayTimeoutSECONDS - displayRedrawSECONDS))
jamfHelperARRAY=("${jamfHelperBackupARRAY[@]}")
done
else
[[ -n $displayTimeoutSECONDS ]] && jamfHelperARRAY+=(-timeout "$displayTimeoutSECONDS" -countdown)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
dialogRESULT=$("$jamfHELPER" "${jamfHelperARRAY[@]}")
dialogRETURN="$?"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRESULT is: $dialogRESULT"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: dialogRETURN is: $dialogRETURN"
fi
}
# MARK: *** Install Now Notifications ***
################################################################################
# Display a non-interactive notification informing the user that the install now workflow has started.
notifyInstallNowStart() {
setDisplayLanguage
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening install now start notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyInstallNowStartTITLE" -subtitle "$notifyInstallNowStartBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening install now start notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyInstallNowStartTITLE" -description "$notifyInstallNowStartBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that the install now workflow is downloading the macOS update/upgrade.
notifyInstallNowDownload() {
setDisplayLanguage
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening install now downloading notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyInstallNowDownloadTITLE" -subtitle "$notifyInstallNowDownloadBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening install now downloading notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyInstallNowDownloadTITLE" -description "$notifyInstallNowDownloadBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that macOS is up to date.
notifyInstallNowUpToDate() {
setDisplayLanguage
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening install now up to date notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyInstallNowUpToDateTITLE" -subtitle "$notifyInstallNowUpToDateBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$okButtonDISPLAY")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening install now up to date notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyInstallNowUpToDateTITLE" -description "$notifyInstallNowUpToDateBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE" -button1 "$okButtonDISPLAY" -defaultButton 1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that the install now workflow has failed.
notifyInstallNowFailure() {
setDisplayLanguage
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening install now failure notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyInstallNowFailureTITLE" -subtitle "$notifyInstallNowFailureBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$okButtonDISPLAY")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening install now failure notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyInstallNowFailureTITLE" -description "$notifyInstallNowFailureBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE" -button1 "$okButtonDISPLAY" -defaultButton 1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# MARK: *** Default Workflow Notifications ***
################################################################################
# Display a non-interactive notification informing the user there is insufficient free space for a macOS update/upgrade.
notifyStorage() {
sendToLog "Warning: Current available storage is at $availableStorageGB GBs which is below the $requiredStorageGB GBs that is required for macOS update/upgrade workflow."
sendToStatus "Running: Notification insufficient free space."
workflowTimeoutSECONDS=$freeSpaceTimeoutSECONDS
setDisplayLanguage
# The initial $ibmNotifierARRAY[] settings for the insufficient free space notification.
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$notifyStorageTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
# The initial $jamfHelperARRAY[] settings for the insufficient free space notification.
jamfHelperARRAY=(-windowType hud -lockHUD -title "$notifyStorageTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
# Variations for the main body text of the insufficient free space notification.
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Date deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyStorageBodyDeadlineDateIBM")
else
sendToLog "jamfHelper Notification: Date deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyStorageBodyDeadlineDateJAMF")
fi
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]] || [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Days deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyStorageBodyDeadlineDaysIBM")
else
sendToLog "jamfHelper Notification: Days deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyStorageBodyDeadlineDaysJAMF")
fi
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Count deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyStorageBodyDeadlineCountIBM")
else
sendToLog "jamfHelper Notification: Count deadline insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyStorageBodyDeadlineCountJAMF")
fi
else # No deadlines, this is the default insufficient free space notification.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Default insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyStorageBodyDefaultIBM")
else
sendToLog "jamfHelper: Default insufficient free space notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyStorageBodyDefaultJAMF")
fi
fi
# Open the appropriate storage assistant and the insufficient free space notification.
if [[ "$macOSMAJOR" -ge 13 ]]; then
sendToLog "Status: Opening the Storage pane of the System Settings.app."
sudo -u "$currentUserNAME" open "x-apple.systempreferences:com.apple.settings.Storage" &
else
sendToLog "Status: Opening the Storage Management.app."
sudo -u "$currentUserNAME" open "/System/Library/CoreServices/Applications/Storage Management.app" &
fi
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
# This handles the $freeSpaceTimeoutSECONDS and $displayRedrawSECONDS options while watching for available free space.
whileTimeoutSECONDS=$workflowTimeoutSECONDS
[[ -n $displayRedrawSECONDS ]] && redrawTimerSECONDS=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: storageRecheckSECONDS is: $storageRecheckSECONDS"
while [[ "$whileTimeoutSECONDS" -ge 0 ]]; do
sleep "$storageRecheckSECONDS"
checkAvailableStorage
if [[ "$storageREADY" == "TRUE" ]]; then
sendToLog "Status: Current available storage is now at $availableStorageGB GBs, the macOS update/upgrade workflow can continue."
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
break
fi
if [[ -n $displayRedrawSECONDS ]]; then
if [[ $redrawTimerSECONDS -ge $displayRedrawSECONDS ]]; then
if [[ "$macOSMAJOR" -ge 13 ]]; then
sudo -u "$currentUserNAME" open "x-apple.systempreferences:com.apple.settings.Storage" &
else
sudo -u "$currentUserNAME" open "/System/Library/CoreServices/Applications/Storage Management.app" &
fi
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
redrawTimerSECONDS=0
else
redrawTimerSECONDS=$((redrawTimerSECONDS+storageRecheckSECONDS))
fi
fi
whileTimeoutSECONDS=$((whileTimeoutSECONDS-storageRecheckSECONDS))
done
# If there still is not sufficient free space, then exit.
if [[ "$storageREADY" == "FALSE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Waiting for user to make more free space available timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Waiting for user to make more free space available timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Waiting for user to make more free space available timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Waiting for user to make more free space available timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
notifyFailure
makeLaunchDaemonCalendar
fi
fi
}
# Display a non-interactive notification informing the user they need to plug the computer into AC power.
notifyPower() {
sendToLog "Warning: Current battery level is at $currentBatteryLEVEL% which is below the minimum required level of $batteryLevelPERCENT%."
sendToStatus "Running: Notification AC power required."
workflowTimeoutSECONDS=$batteryTimeoutSECONDS
setDisplayLanguage
# The initial $ibmNotifierARRAY[] settings for the power required notification.
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$notifyPowerTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
# The initial $jamfHelperARRAY[] settings for the power required notification.
jamfHelperARRAY=(-windowType hud -lockHUD -title "$notifyPowerTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
# Variations for the main body text of the power required notification.
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Date deadline power required notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyPowerBodyDeadlineDateIBM")
else
sendToLog "jamfHelper Notification: Date deadline power required notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyPowerBodyDeadlineDateJAMF")
fi
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]] || [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Days deadline power required notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyPowerBodyDeadlineDaysIBM")
else
sendToLog "jamfHelper Notification: Days deadline power required notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyPowerBodyDeadlineDaysJAMF")
fi
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Count deadline power required notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyPowerBodyDeadlineCountIBM")
else
sendToLog "jamfHelper Notification: Count deadline power required notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyPowerBodyDeadlineCountJAMF")
fi
else # No deadlines, this is the default power required notification.
if [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Default power required notification with a $workflowTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$notifyPowerBodyDefaultIBM")
else
sendToLog "jamfHelper: Default power required notification with a $workflowTimeoutSECONDS second timeout."
jamfHelperARRAY+=(-description "$notifyPowerBodyDefaultJAMF")
fi
fi
# Open the initial power required notification.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
# This handles waiting for AC power along with the $batteryTimeoutSECONDS and $displayRedrawSECONDS options.
acPOWER="FALSE"
whileTimeoutSECONDS=$workflowTimeoutSECONDS
[[ -n $displayRedrawSECONDS ]] && redrawTimerSECONDS=0
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: powerRecheckSECONDS is: $powerRecheckSECONDS"
while [[ "$whileTimeoutSECONDS" -ge 0 ]]; do
sleep "$powerRecheckSECONDS"
[[ $(pmset -g ps | grep -ic 'AC Power') -ne 0 ]] && acPOWER="TRUE"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: acPOWER: $acPOWER"
if [[ "$acPOWER" == "TRUE" ]]; then
sendToLog "Status: AC power detected, the macOS update/upgrade workflow can continue."
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
break
fi
if [[ -n $displayRedrawSECONDS ]]; then
if [[ $redrawTimerSECONDS -ge $displayRedrawSECONDS ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
redrawTimerSECONDS=0
else
redrawTimerSECONDS=$((redrawTimerSECONDS+powerRecheckSECONDS))
fi
fi
whileTimeoutSECONDS=$((whileTimeoutSECONDS-powerRecheckSECONDS))
done
# If there still is no AC power, then exit.
if [[ "$acPOWER" == "FALSE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Waiting for user to connect AC power timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Waiting for user to connect AC power timed out after $workflowTimeoutSECONDS seconds, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Waiting for user to connect AC power timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Waiting for user to connect AC power timed out after $workflowTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
notifyFailure
makeLaunchDaemonCalendar
fi
fi
}
# Display a non-interactive notification informing the user that an update/upgrade that requires preparation has started.
notifyPrepare() {
setDisplayLanguage
# The initial $ibmNotifierARRAY[] settings for the preparing update notification.
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyPrepTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
# The initial $jamfHelperARRAY[] settings for the preparing update notification.
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyPrepTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
# Variations for the main body text of the preparing update notification.
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Date deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
ibmNotifierARRAY+=(-subtitle "$notifyPrepBodyDeadlineDateIBM")
else
sendToLog "jamfHelper Notification: Date deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
jamfHelperARRAY+=(-description "$notifyPrepBodyDeadlineDateJAMF")
fi
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]] || [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Days deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
ibmNotifierARRAY+=(-subtitle "$notifyPrepBodyDeadlineDaysIBM")
else
sendToLog "jamfHelper Notification: Days deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
jamfHelperARRAY+=(-description "$notifyPrepBodyDeadlineDaysJAMF")
fi
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Count deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
ibmNotifierARRAY+=(-subtitle "$notifyPrepBodyDeadlineCountIBM")
else
sendToLog "jamfHelper Notification: Count deadline preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
jamfHelperARRAY+=(-description "$notifyPrepBodyDeadlineCountJAMF")
fi
else # No deadlines, this is the default preparing update notification.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Default preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
ibmNotifierARRAY+=(-subtitle "$notifyPrepBodyDefaultIBM")
else
sendToLog "jamfHelper: Default preparing update notification showing a $prepareTimeEstimateDISPLAY minute estimate."
jamfHelperARRAY+=(-description "$notifyPrepBodyDefaultJAMF")
fi
fi
# Open notification in the background allowing super to continue.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that the computer going to restart soon.
notifyRestart() {
setDisplayLanguage
# The initial $ibmNotifierARRAY[] settings for the restart notification.
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyRestartTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
# The initial $jamfHelperARRAY[] settings for the restart notification.
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyRestartTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
# Variations for the main body text of the restart notification.
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Date deadline restart soon notification."
ibmNotifierARRAY+=(-subtitle "$notifyRestartBodyDeadlineDateIBM")
else
sendToLog "jamfHelper Notification: Date deadline restart soon notification."
jamfHelperARRAY+=(-description "$notifyRestartBodyDeadlineDateJAMF")
fi
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]] || [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Days deadline restart soon notification."
ibmNotifierARRAY+=(-subtitle "$notifyRestartBodyDeadlineDaysIBM")
else
sendToLog "jamfHelper Notification: Days deadline restart soon notification."
jamfHelperARRAY+=(-description "$notifyRestartBodyDeadlineDaysJAMF")
fi
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Count deadline restart soon notification."
ibmNotifierARRAY+=(-subtitle "$notifyRestartBodyDeadlineCountIBM")
else
sendToLog "jamfHelper Notification: Count deadline restart soon notification."
jamfHelperARRAY+=(-description "$notifyRestartBodyDeadlineCountJAMF")
fi
else # No deadlines, this is the default restart notification.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Default restart soon notification."
ibmNotifierARRAY+=(-subtitle "$notifyRestartBodyDefaultIBM")
else
sendToLog "jamfHelper: Default restart soon notification."
jamfHelperARRAY+=(-description "$notifyRestartBodyDefaultJAMF")
fi
fi
# Open notification in the background allowing super to continue.
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
openNotifyIbmNotifier
else
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that the install now workflow is installing recommended (non-system) software updates.
notifyRecommended() {
setDisplayLanguage
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening install now installing recommended (non-system) software updates notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyRecommendedTITLE" -subtitle "$notifyRecommendedBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -accessory_view_type progressbar -accessory_view_payload "/percent indeterminate")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening install now installing recommended (non-system) software updates notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyRecommendedTITLE" -description "$notifyRecommendedBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# Display a non-interactive notification informing the user that update process has failed.
notifyFailure() {
setDisplayLanguage
setDeferButton
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
sendToLog "IBM Notifier: Opening update failure notification..."
ibmNotifierARRAY=(-type popup -always_on_top -position top_right -bar_title "$notifyFailureTITLE" -subtitle "$notifyFailureBodyIBM" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$deferButtonDISPLAY")
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: ibmNotifierARRAY[] is:\n${ibmNotifierARRAY[*]}"
openNotifyIbmNotifier
else
sendToLog "jamfHelper: Opening update failure notification..."
jamfHelperARRAY=(-windowType hud -windowPosition ur -lockHUD -title "$notifyFailureTITLE" -description "$notifyFailureBodyJAMF" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE" -button1 "$deferButtonDISPLAY" -defaultButton 1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfHelperARRAY[] is:\n${jamfHelperARRAY[*]}"
openNotifyJamfHelper
fi
}
# MARK: *** Interactive Dialogs ***
################################################################################
# Display an interactive dialog with restart and deferral options. This sets $choiceINSTALL and if $menuDeferSECONDS then also sets $defaultDeferSECONDS.
dialogRestartOrDefer() {
setDisplayLanguage
sendToStatus "Running: Dialog restart or defer."
[[ -n $deferDialogTimeoutSECONDS ]] && displayTimeoutSECONDS=$deferDialogTimeoutSECONDS
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
[[ -n $deferDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: Ask for restart or defer dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $deferDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: Ask for restart or defer dialog with no timeout."
# Create initial $ibmNotifierARRAY[] settings for the dialog.
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$dialogRestartOrDeferTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -secondary_button_label "$restartButtonDISPLAY")
# Body text variations based on deadline options.
if [[ -n "$deadlineDISPLAY" ]] && [[ -n "$countDISPLAY" ]]; then # Show both date and maximum deferral count deadlines.
ibmNotifierARRAY+=(-subtitle "$dialogRestartOrDeferBodyDateCountIBM")
elif [[ -n "$deadlineDISPLAY" ]]; then # Show only date deadline.
ibmNotifierARRAY+=(-subtitle "$dialogRestartOrDeferBodyDateIBM")
elif [[ -n "$countDISPLAY" ]]; then # Show only maximum deferral count deadline.
ibmNotifierARRAY+=(-subtitle "$dialogRestartOrDeferBodyCountIBM")
else # Show no deadlines.
ibmNotifierARRAY+=(-subtitle "$dialogRestartOrDeferBodyUnlimitedIBM")
fi
displayTimeoutTEXT="$dialogRestartOrDeferTimeoutIBM"
# If needed, handle the $menuDeferSECONDS option and set $menuDisplayTEXT.
if [[ -n $menuDeferSECONDS ]]; then
oldIFS="$IFS"; IFS=','
read -r -a menuDeferSecondsARRAY <<< "$menuDeferSECONDS"
read -r -a menuDeferDisplayARRAY <<< "$menuDeferSECONDS"
for i in "${!menuDeferDisplayARRAY[@]}"; do
if [[ ${menuDeferDisplayARRAY[i]} -lt 3600 ]]; then
menuDeferDisplayARRAY[i]="$((menuDeferDisplayARRAY[i] / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ ${menuDeferDisplayARRAY[i]} -eq 3600 ]]; then
menuDeferDisplayARRAY[i]="1 $dialogRestartOrDeferHourDISPLAY"
elif [[ ${menuDeferDisplayARRAY[i]} -gt 3600 ]] && [[ ${menuDeferDisplayARRAY[i]} -lt 7200 ]]; then
menuDeferDisplayARRAY[i]="1 $dialogRestartOrDeferHourDISPLAY $((menuDeferDisplayARRAY[i] % 3600 / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ ${menuDeferDisplayARRAY[i]} -ge 7200 ]] && [[ ${menuDeferDisplayARRAY[i]} -lt 86400 ]] && [[ $((menuDeferDisplayARRAY[i] % 3600)) -eq 0 ]]; then
menuDeferDisplayARRAY[i]="$((menuDeferDisplayARRAY[i] / 3600)) $dialogRestartOrDeferHoursDISPLAY"
elif [[ ${menuDeferDisplayARRAY[i]} -gt 7200 ]] && [[ ${menuDeferDisplayARRAY[i]} -lt 86400 ]] && [[ $((menuDeferDisplayARRAY[i] % 3600)) -ne 0 ]]; then
menuDeferDisplayARRAY[i]="$((menuDeferDisplayARRAY[i] / 3600)) $dialogRestartOrDeferHoursDISPLAY $((menuDeferDisplayARRAY[i] % 3600 / 60)) $dialogRestartOrDeferMinutesDISPLAY"
elif [[ ${menuDeferDisplayARRAY[i]} -eq 86400 ]]; then
menuDeferDisplayARRAY[i]="1 $dialogRestartOrDeferDayDISPLAY"
fi
done
IFS=$'\n'
menuDisplayTEXT="${menuDeferDisplayARRAY[*]}"
IFS="$oldIFS"
else # Instead, add a defer button containing the text based off $defaultDeferSECONDS time.
deferSECONDS="$defaultDeferSECONDS"
setDeferButton
fi
ibmNotifierARRAY+=(-main_button_label "$deferButtonDISPLAY")
# Start the dialog.
openDialogIbmNotifier
# The $dialogRETURN contains the IBM Notifier.app return code. If $menuDeferSECONDS was enabled then set $deferSECONDS.
case "$dialogRETURN" in
0)
choiceINSTALL="FALSE"
if [[ -n $menuDeferSECONDS ]]; then
deferSECONDS="${menuDeferSecondsARRAY[$dialogRESULT]}"
sendToLog "Status: User chose to defer update for $deferSECONDS seconds."
sendToStatus "Pending: User chose to defer update for $deferSECONDS seconds."
else
deferSECONDS="$defaultDeferSECONDS"
sendToLog "Status: User chose to defer update, using the default defer of $deferSECONDS seconds."
sendToStatus "Pending: User chose to defer update, using the default defer of $deferSECONDS seconds."
fi
;;
4|255)
choiceINSTALL="FALSE"
if [[ -n $menuDeferSECONDS ]]; then
deferSECONDS="${menuDeferSecondsARRAY[$dialogRESULT]}"
sendToLog "Status: Display timeout automatically chose to defer update for $deferSECONDS seconds."
sendToStatus "Pending: Display timeout automatically chose to defer update for $deferSECONDS seconds."
else
deferSECONDS="$defaultDeferSECONDS"
sendToLog "Status: Display timeout automatically chose to defer update, using the default defer of $deferSECONDS seconds."
sendToStatus "Pending: Display timeout automatically chose to defer update, using the default defer of $deferSECONDS seconds."
fi
;;
2)
sendToLog "Status: User chose to restart now."
choiceINSTALL="TRUE"
;;
esac
else
[[ -n $deferDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: Ask for restart or defer dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $deferDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: Ask for restart or defer dialog with no timeout."
# Create initial $jamfHelperARRAY[] settings for the dialog.
jamfHelperARRAY=(-windowType utility -title "$dialogRestartOrDeferTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE" -button1 "$deferButtonDISPLAY" -button2 "$restartButtonDISPLAY" -defaultButton 1 -cancelButton 2)
# Body text variations based on deadline options. Note that any invisible characters (tabs and new line) are "shown" in the jamfHelper dialog.
if [[ -n "$deadlineDISPLAY" ]] && [[ -n "$countDISPLAY" ]]; then # Show both date and maximum deferral count deadlines.
jamfHelperARRAY+=(-description "$dialogRestartOrDeferBodyDateCountJAMF")
elif [[ -n "$deadlineDISPLAY" ]]; then # Show only date deadline.
jamfHelperARRAY+=(-description "$dialogRestartOrDeferBodyDateJAMF")
elif [[ -n "$countDISPLAY" ]]; then # Show only maximum deferral count deadline.
jamfHelperARRAY+=(-description "$dialogRestartOrDeferBodyCountJAMF")
else # Show no deadlines.
jamfHelperARRAY+=(-description "$dialogRestartOrDeferBodyUnlimitedJAMF")
fi
# If needed, add the $menuDeferSECONDS option to the $jamfHelperARRAY[].
if [[ -n $menuDeferSECONDS ]]; then
menuDeferSECONDS=$(echo "$menuDeferSECONDS" | sed 's/,/, /g')
jamfHelperARRAY+=(-showDelayOptions "$menuDeferSECONDS")
fi
# Start the dialog.
openDialogJamfHelper
# The $dialogRESULT contains the user's selection; "0" or "1" for deferral and "2" for restart. If $menuDeferSECONDS was enabled then set $deferSECONDS.
case "$dialogRESULT" in
0 | 1 | *1)
choiceINSTALL="FALSE"
if [[ -n $menuDeferSECONDS ]]; then
deferSECONDS=$(echo "$dialogRESULT" | sed 's/.$//')
sendToLog "Status: User or display timeout chose to defer update for $deferSECONDS seconds."
sendToStatus "Pending: User or display timeout chose to defer update for $deferSECONDS seconds."
else
deferSECONDS="$defaultDeferSECONDS"
sendToLog "Status: User or display timeout chose to defer update, using the default defer of $deferSECONDS seconds."
sendToStatus "Pending: User or display timeout chose to defer update, using the default defer of $deferSECONDS seconds."
fi
;;
*2)
sendToLog "Status: User chose to restart now."
choiceINSTALL="TRUE"
;;
esac
fi
}
# Display an interactive dialog when a soft deadline has passed, giving the user only one button to continue the workflow.
dialogSoftDeadline() {
setDisplayLanguage
sendToStatus "Running: Dialog soft deadline."
[[ -n $softDialogTimeoutSECONDS ]] && displayTimeoutSECONDS=$softDialogTimeoutSECONDS
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
# The initial $ibmNotifierARRAY[] settings for the soft deadline dialog.
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$dialogSoftDeadlineTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$restartButtonDISPLAY")
# Variations for the main body text of the soft deadline dialog.
if [[ "$deadlineDateSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft date deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft date deadline dialog with no timeout."
ibmNotifierARRAY+=(-subtitle "$dialogSoftDeadlineBodyDateIBM")
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft days deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft days deadline dialog with no timeout."
ibmNotifierARRAY+=(-subtitle "$dialogSoftDeadlineBodyDaysIBM")
elif [[ "$deadlineCountSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft count deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "IBM Notifier: soft count deadline dialog with no timeout."
ibmNotifierARRAY+=(-subtitle "$dialogSoftDeadlineBodyCountIBM")
fi
displayTimeoutTEXT="$dialogSoftDeadlineTimeoutIBM"
# Start the dialog.
unset menuDeferSECONDS
openDialogIbmNotifier
# The $dialogRETURN contains the IBM Notifier.app return code.
case "$dialogRETURN" in
0)
sendToLog "Status: User chose to restart."
;;
255)
sendToLog "Status: Display timeout automatically chose to restart."
;;
esac
else
# The initial $jamfHelperARRAY[] settings for the soft deadline dialog.
jamfHelperARRAY=(-windowType utility -title "$dialogSoftDeadlineTITLE" -icon "$cachedICON" -iconSize "$jamfHelperIconSIZE" -button1 "$restartButtonDISPLAY" -defaultButton 1)
# Variations for the main body text of the soft deadline dialog
if [[ "$deadlineDateSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft date deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft date deadline dialog with no timeout."
jamfHelperARRAY+=(-description "$dialogSoftDeadlineBodyDateJAMF")
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft days deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft days deadline dialog with no timeout."
jamfHelperARRAY+=(-description "$dialogSoftDeadlineBodyDaysJAMF")
elif [[ "$deadlineCountSTATUS" == "SOFT" ]]; then
[[ -n $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft count deadline dialog with a $displayTimeoutSECONDS second timeout."
[[ -z $softDialogTimeoutSECONDS ]] && sendToLog "jamfHelper: soft count deadline dialog with no timeout."
jamfHelperARRAY+=(-description "$dialogSoftDeadlineBodyCountJAMF")
fi
# Start the dialog.
openDialogJamfHelper
sendToLog "Status: User or display timeout accepted soft deadline dialog."
fi
}
# Display an interactive IBM Notifier dialog to collect user credentials for macOS update/upgrade workflow.
dialogUserAuth() {
# Check to make sure the current user is a valid volume owner.
if [[ "$currentUserVolumeOWNER" == "FALSE" ]]; then
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Current user \"$currentUserNAME\" is not a volume owner, install now workflow can not continue."
sendToStatus "Inactive Error: Current user \"$currentUserNAME\" is not a volume owner, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Current user \"$currentUserNAME\" is not a volume owner, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Current user \"$currentUserNAME\" is not a volume owner, trying again in $deferSECONDS seconds."
kickSoftwareUpdateD
makeLaunchDaemonCalendar
fi
fi
# The initial $ibmNotifierARRAY[] and variations for the main body text for the user authentication dialog.
setDisplayLanguage
sendToStatus "Running: Dialog user authentication."
displayTimeoutSECONDS=$userAuthTimeoutSECONDS
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$dialogUserAuthTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$restartButtonDISPLAY")
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
sendToLog "IBM Notifier: Date deadline user authentication dialog with a $displayTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthDeadlineDateBODY")
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]]; then
sendToLog "IBM Notifier: Soft days deadline user authentication dialog with a $displayTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthDeadlineSoftDaysBODY")
elif [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
sendToLog "IBM Notifier: Hard days deadline user authentication dialog with a $displayTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthDeadlineHardDaysBODY")
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
sendToLog "IBM Notifier: Count deadline user authentication dialog with a $displayTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthDeadlineCountBODY")
else # No deadlines, this is the default user authentication dialog.
sendToLog "IBM Notifier: Default user authentication dialog with a $displayTimeoutSECONDS second timeout."
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthDefaultBODY")
fi
secureAccessoryPAYLOAD="/title $dialogUserAuthPassTITLE /placeholder $dialogUserAuthPassPLACEHOLDER /required"
# Open the user authentication dialog including handling of the $userAuthTimeoutSECONDS and user password validation.
if [[ -n $displayAccessoryUserAuthCONTENT ]]; then
[[ -n $displayAccessoryCONTENT ]] && displayAccessoryBackupCONTENT="$displayAccessoryCONTENT"
displayAccessoryCONTENT="$displayAccessoryUserAuthCONTENT"
fi
userAuthMODE="TRUE"
userAuthREADY="FALSE"
userAuthATTEMPT=0
while [[ $displayTimeoutSECONDS -ge 0 ]]; do
if [[ $userAuthATTEMPT -eq 1 ]]; then
# Re-create $ibmNotifierARRAY[] settings for the user authentication dialog when the user authentication fails.
ibmNotifierARRAY=(-type popup -always_on_top -bar_title "$dialogUserAuthTITLE" -icon_path "$cachedICON" -icon_width "$ibmNotifierIconSIZE" -icon_height "$ibmNotifierIconSIZE" -main_button_label "$restartButtonDISPLAY")
if [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDateSTATUS" == "HARD" ]]; then
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthRetryDeadlineDateBODY")
elif [[ "$deadlineDaysSTATUS" == "SOFT" ]]; then
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthRetryDeadlineSoftDaysBODY")
elif [[ "$deadlineDaysSTATUS" == "HARD" ]]; then
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthRetryDeadlineHardDaysBODY")
elif [[ "$deadlineCountSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthRetryDeadlineCountBODY")
else # No deadlines, this is the default user authentication dialog.
ibmNotifierARRAY+=(-subtitle "$dialogUserAuthRetryDefaultBODY")
fi
secureAccessoryPAYLOAD="/title $dialogUserAuthRetryPassTITLE /placeholder $dialogUserAuthRetryPassPLACEHOLDER /required"
fi
openDialogIbmNotifier
((userAuthATTEMPT++))
if [[ $dialogRETURN -eq 0 ]]; then
if [[ "$(dscl /Local/Default -authonly "$currentUserNAME" "$dialogRESULT" 2>&1)" == "" ]]; then
installACCOUNT="$currentUserNAME"
installPASSWORD="$dialogRESULT"
userAuthREADY="TRUE"
break
fi
else
break
fi
done
if [[ -n $displayAccessoryBackupCONTENT ]]; then
displayAccessoryCONTENT="$displayAccessoryBackupCONTENT"
else
unset displayAccessoryCONTENT
fi
unset userAuthMODE
# If user authentication was successful then evaluate option to fix bootstrap token.
if [[ "$userAuthREADY" == "TRUE" ]]; then
sendToLog "Status: Current user \"$currentUserNAME\" credentials verified."
if [[ "$mdmENROLLED" == "TRUE" ]] && [[ "$bootstrapTOKEN" != "TRUE" ]] && { [[ "$userAuthMDMFAILOVER" == "TRUE" ]] || [[ "$userAuthMDMFailoverBOOTSTRAP" == "TRUE" ]]; }; then
attemptBootstrapFIX="TRUE"
if [[ "$currentUserADMIN" == "FALSE" ]]; then
sendToLog "Warning: Local user account \"$currentUserNAME\" can not be used to escrow bootstrap token because they are not a local admin."
attemptBootstrapFIX="FALSE"
fi
if [[ "$currentUserSecureTOKEN" == "FALSE" ]]; then
sendToLog "Warning: Local user account \"$currentUserNAME\" can not be used to escrow bootstrap token because they do not have a secure token."
attemptBootstrapFIX="FALSE"
fi
if [[ "$mdmSERVICE" == "FALSE" ]]; then
sendToLog "Warning: Can not escrow bootstrap token because the MDM service is not available."
attemptBootstrapFIX="FALSE"
fi
if [[ "$attemptBootstrapFIX" == "TRUE" ]]; then
sendToLog "Status: Attempting to use the credentials from user \"$currentUserNAME\" to escrow bootstrap token..."
commandRESULT=$(profiles install -type bootstraptoken -user "$currentUserNAME" -password "$installPASSWORD" 2>&1)
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: commandRESULT is: $commandRESULT"
checkBootstrapToken
fi
fi
else # The user authentication dialog timed out.
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Error: Waiting for user authentication timed out after $displayTimeoutSECONDS seconds, install now workflow can not continue."
sendToStatus "Inactive Error: Waiting for user authentication timed out after $displayTimeoutSECONDS seconds, install now workflow can not continue."
notifyInstallNowFailure
errorExit
else
deferSECONDS="$errorDeferSECONDS"
sendToLog "Error: Waiting for user authentication timed out after $displayTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
sendToStatus "Pending: Waiting for user authentication timed out after $displayTimeoutSECONDS seconds, trying again in $deferSECONDS seconds."
notifyFailure
makeLaunchDaemonCalendar
fi
fi
}
# MARK: *** Main Workflow ***
################################################################################
mainWorkflow() {
# Initial super workflow preparations.
checkRoot
setDefaults
superInstallation
getOptions "$@"
superStartup "$@"
# If restarting from a macOS update/upgrade this workflow starts before all others.
if [[ "$restartVALIDATE" == "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION RESTART VALIDATION ****"
sendToStatus "Running: macOS update/upgrade restart validation."
checkAfterRestart
fi
# If requested then restart counters.
[[ "$restartDAYS" == "TRUE" ]] && restartZeroDay
[[ "$restartCOUNTS" == "TRUE" ]] && restartDeferralCounters
# Prepare for alternate self-service workflow modes.
[[ "$testModeOPTION" == "TRUE" ]] && sendToLog "Startup: Test mode enabled with $testModeTimeoutSECONDS second timeout."
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "Startup: Install now mode enabled."
notifyInstallNowStart
if [[ "$testModeOPTION" == "TRUE" ]]; then
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the install now start notification..."
sleep "$testModeTimeoutSECONDS"
fi
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "Startup: Only download mode enabled."
fi
[[ -n $policyTRIGGERS ]] && sendToLog "Startup: Jamf Pro Policy triggers: $policyTRIGGERS"
[[ "$restartWithoutUpdatesOPTION" == "TRUE" ]] && sendToLog "Warning: Restart without updates option is enabled, this computer will restart if there is no macOS update or upgrade available."
# Start the appropriate main workflow based on user options.
if [[ "$skipUpdatesOPTION" == "TRUE" ]]; then # Skip software updates/upgrade mode option.
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION SKIP UPDATES/UPGRADE ****"
softwareUpdateRECOMMENDED="FALSE"
macOSUpgradeVersionTARGET="FALSE"
softwareUpdateMACOS="FALSE"
if [[ "$testModeOPTION" == "TRUE" ]]; then
sendToLog "Test Mode: Simulating skip updates workflow."
{ [[ "$restartWithoutUpdatesOPTION" != "TRUE" ]] && [[ -z $policyTRIGGERS ]]; } && sendToLog "Warning: You need to also use --restart-without-updates or --policy-triggers to simulate notification and dialog workflows."
fi
else # Default software update/upgrade workflows.
# Check for updates/upgrades.
if [[ "$testModeOPTION" != "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION CHECK SOFTWARE UPDATES/UPGRADE ****"
[[ "$fullCheckREQUIRED" != "TRUE" ]] && checkSoftwareListCache
checkSoftwareUpdates
checkMacOSUpgrades
checkMacOSDownloads
else # Test mode.
softwareUpdateRECOMMENDED="FALSE"
macOSUpgradeVersionTARGET="FALSE"
softwareUpdateMACOS="FALSE"
if [[ "$enforceNonSystemUpdatesOPTION" == "TRUE" ]]; then
softwareUpdateRECOMMENDED="TRUE"
sendToLog "Test Mode: Simulating enforce all non-system updates workflow."
elif [[ "$allowUpgradeOPTION" == "TRUE" ]]; then
macOSUpgradeVersionTARGET="$macOSMAJOR"
macOSSoftwareUpgradeVersionTARGET="$macOSMAJOR.$macOSMINOR"
macOSInstallerNameTARGET="$macOSNAME"
macOSInstallerVersionTARGET="$macOSMAJOR.$macOSMINOR"
macOSInstallerBuildTARGET="$macOSBUILD"
macOSSoftwareUpgradeGB=10
macOSSoftwareUpdateDownloadREQUIRED="TRUE"
macOSInstallerDownloadREQUIRED="TRUE"
sendToLog "Test Mode: Simulating macOS $macOSUpgradeVersionTARGET upgrade workflow."
elif [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]]; then
sendToLog "Test Mode: Simulating restart without updates workflow."
else # Simulate a macOS update.
softwareUpdateMACOS="TRUE"
macOSSoftwareUpdateLABEL="mac OS Label"
macOSSoftwareUpdateTITLE="mac OS Title"
macOSSoftwareUpdateVERSION="$macOSMAJOR.$macOSMINOR"
macOSSoftwareUpdateGB=5
macOSSoftwareUpdateDownloadREQUIRED="TRUE"
sendToLog "Test Mode: Simulating a macOS $macOSSoftwareUpdateVERSION update workflow."
fi
fi
deleteUnneededMacOSInstallers # This function includes internal test mode logic.
# At this point all available updates/upgrades have been evaluated so report different workflow statuses.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: upgradeWORKFLOW is: $upgradeWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: updateWORKFLOW is: $updateWORKFLOW"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateRECOMMENDED is: $softwareUpdateRECOMMENDED"
if [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]]; then # A macOS upgrade is available and option to allow upgrade is enabled.
if [[ $macOSVERSION -ge 1203 ]] && [[ "$upgradeWORKFLOW" != "JAMF" ]]; then # macOS 12.3 or newer can upgrade via softwareupdate, unless using MDM workflow.
if [[ "$installNowOPTION" == "TRUE" ]]; then
[[ "$upgradeWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPGRADE MACOS $macOSSoftwareUpgradeVersionTARGET SOFTWAREUPDATE ****"
[[ "$upgradeWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPGRADE MACOS $macOSSoftwareUpgradeVersionTARGET USER AUTHENTICATION ****"
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION ONLY DOWNLOAD UPGRADE MACOS $macOSSoftwareUpgradeVersionTARGET SOFTWAREUPDATE ****"
else # Default super workflow.
[[ "$upgradeWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE MACOS $macOSSoftwareUpgradeVersionTARGET SOFTWAREUPDATE ****"
[[ "$upgradeWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE MACOS $macOSSoftwareUpgradeVersionTARGET USER AUTHENTICATION ****"
fi
else # Systems older than macOS 12.3 or using the MDM workflow upgrade via installer.
if [[ "$installNowOPTION" == "TRUE" ]]; then
[[ "$upgradeWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPGRADE MACOS $macOSInstallerVersionTARGET INSTALLER ****"
[[ "$upgradeWORKFLOW" == "JAMF" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPGRADE MACOS $macOSInstallerVersionTARGET MDM PUSH ****"
[[ "$upgradeWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPGRADE MACOS $macOSInstallerVersionTARGET USER AUTHENTICATION ****"
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION ONLY DOWNLOAD UPGRADE MACOS $macOSInstallerVersionTARGET INSTALLER ****"
else # Default super workflow.
[[ "$upgradeWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE MACOS $macOSInstallerVersionTARGET INSTALLER ****"
[[ "$upgradeWORKFLOW" == "JAMF" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE MACOS $macOSInstallerVersionTARGET MDM PUSH ****"
[[ "$upgradeWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPGRADE MACOS $macOSInstallerVersionTARGET USER AUTHENTICATION ****"
fi
fi
if [[ -n $displayAccessoryUpgradeCONTENT ]]; then
displayAccessoryCONTENT="$displayAccessoryUpgradeCONTENT"
elif [[ -n $displayAccessoryDefaultCONTENT ]]; then
displayAccessoryCONTENT="$displayAccessoryDefaultCONTENT"
fi
elif [[ "$softwareUpdateMACOS" == "TRUE" ]]; then # macOS updates are available.
if [[ "$installNowOPTION" == "TRUE" ]]; then
[[ "$updateWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPDATE MACOS $macOSSoftwareUpdateVERSION SOFTWAREUPDATE ****"
[[ "$updateWORKFLOW" == "JAMF" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPDATE MACOS $macOSSoftwareUpdateVERSION MDM PUSH ****"
[[ "$updateWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPDATE MACOS $macOSSoftwareUpdateVERSION USER AUTHENTICATION ****"
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION ONLY DOWNLOAD UPDATE MACOS $macOSSoftwareUpdateVERSION SOFTWAREUPDATE ****"
else # Default super workflow.
[[ "$updateWORKFLOW" == "LOCAL" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE MACOS $macOSSoftwareUpdateVERSION SOFTWAREUPDATE ****"
[[ "$updateWORKFLOW" == "JAMF" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE MACOS $macOSSoftwareUpdateVERSION MDM PUSH ****"
[[ "$updateWORKFLOW" == "USER" ]] && sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE MACOS $macOSSoftwareUpdateVERSION USER AUTHENTICATION ****"
fi
if [[ -n $displayAccessoryUpdateCONTENT ]]; then
displayAccessoryCONTENT="$displayAccessoryUpdateCONTENT"
elif [[ -n $displayAccessoryDefaultCONTENT ]]; then
displayAccessoryCONTENT="$displayAccessoryDefaultCONTENT"
fi
elif [[ "$softwareUpdateRECOMMENDED" == "TRUE" ]]; then # Only recommended (non-system) updates are available.
defaults delete "$superPLIST" ZeroDayAuto 2> /dev/null
defaults delete "$superPLIST" FocusCounter 2> /dev/null
defaults delete "$superPLIST" SoftCounter 2> /dev/null
defaults delete "$superPLIST" HardCounter 2> /dev/null
if [[ "$enforceNonSystemUpdatesOPTION" == "TRUE" ]]; then # Option to enforce all software updates is enabled.
if [[ "$installNowOPTION" == "TRUE" ]]; then
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION INSTALL NOW UPDATE RECOMMENDED (NON-SYSTEM) SOFTWARE ****"
installRecommendedWorkflow # This function includes internal install now and test mode logic.
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then
sendToLog "Warning: The --enforce-non-system-updates option is ignored when used with the --only-download option."
else # Default super workflow.
sendToLog "**** S.U.P.E.R.M.A.N. $superVERSION UPDATE RECOMMENDED (NON-SYSTEM) SOFTWARE ****"
installRecommendedWorkflow # This function includes internal install now and test mode logic.
fi
else
sendToLog "Status: Available recommended (non-system) software updates are not enforced. You must use the --enforce-non-system-updates option for these items to install without a macOS update/upgrade."
fi
else # No software updates/upgrade needed so clean up any leftover deferral counters.
defaults delete "$superPLIST" ZeroDayAuto 2> /dev/null
defaults delete "$superPLIST" FocusCounter 2> /dev/null
defaults delete "$superPLIST" SoftCounter 2> /dev/null
defaults delete "$superPLIST" HardCounter 2> /dev/null
fi
fi
# This is the main logic for determining what to do in the case of available macOS updates, macOS upgrades, $policyTRIGGERS, or the $restartWithoutUpdatesOPTION.
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: softwareUpdateMACOS is: $softwareUpdateMACOS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: macOSUpgradeVersionTARGET is: $macOSUpgradeVersionTARGET"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: policyTRIGGERS is: $policyTRIGGERS"
[[ "$verboseModeOPTION" == "TRUE" ]] && sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: restartWithoutUpdatesOPTION is: $restartWithoutUpdatesOPTION"
if [[ "$softwareUpdateMACOS" == "TRUE" ]] || [[ "$macOSUpgradeVersionTARGET" != "FALSE" ]] || [[ -n $policyTRIGGERS ]] || [[ "$restartWithoutUpdatesOPTION" == "TRUE" ]]; then
# Check for current user and required storage free space before continuing.
checkCurrentUser
if [[ "$installNowOPTION" == "TRUE" ]]; then # Install now workflow is only possible if the user is logged in.
if [[ "$skipUpdatesOPTION" != "TRUE" ]]; then
[[ "$userAuthMDMFailoverINSTALLNOW" == "TRUE" ]] && userAuthMDMFAILOVER="TRUE"
{ [[ "$upgradeWORKFLOW" == "USER" ]] || [[ "$updateWORKFLOW" == "USER" ]]; } && dialogUserAuth
downloadMacOSWorkflow # This function only downloads if needed and includes internal storage checks and test mode logic.
fi
installRestartWorkflowActiveUser # This function includes internal power/storage checks and test mode logic.
elif [[ "$onlyDownloadOPTION" == "TRUE" ]]; then # Only download workflow doesn't matter if the user is logged in.
[[ -n $policyTRIGGERS ]] && sendToLog "Warning: The --policy-triggers option is ignored when used with the --only-download option."
downloadMacOSWorkflow # This function only downloads if needed and includes internal storage checks and test mode logic.
else # Default super workflow, can run with or without logged in user.
if [[ "$currentUserNAME" == "FALSE" ]]; then # A normal user is not logged in, start all downloads and installations immediately.
installRestartWorkflowNoUser # This function includes internal power/storage checks and test mode logic.
else # A normal user is currently logged in.
# First start by checking deadlines.
checkZeroDay
checkDateDeadlines
checkDaysDeadlines
# User Focus only needs to be checked if there are no date or day deadlines.
if [[ "$deadlineDateSTATUS" == "FALSE" ]] && [[ "$deadlineDaysSTATUS" == "FALSE" ]]; then
checkUserFocus
else # At this point any date or days deadline would rule out any $focusDEFER option.
focusDEFER="FALSE"
fi
checkCountDeadlines
# At this point all deferral and deadline options have been evaluated.
[[ "$skipUpdatesOPTION" != "TRUE" ]] && downloadMacOSWorkflow # This function only downloads if needed and includes internal storage checks and test mode logic.
# At this point all deferral and deadline options have been evaluated.
if [[ "$deadlineDateSTATUS" == "HARD" ]] || [[ "$deadlineDaysSTATUS" == "HARD" ]] || [[ "$deadlineCountSTATUS" == "HARD" ]]; then # A hard deadline has passed, similar to no logged in user but with a notification.
[[ "$userAuthMDMFailoverHARD" == "TRUE" ]] && userAuthMDMFAILOVER="TRUE"
installRestartWorkflowActiveUser # This function includes internal power/storage checks and test mode logic.
elif [[ "$deadlineDateSTATUS" == "SOFT" ]] || [[ "$deadlineDaysSTATUS" == "SOFT" ]] || [[ "$deadlineCountSTATUS" == "SOFT" ]]; then # A soft deadline has passed.
[[ "$userAuthMDMFailoverSOFT" == "TRUE" ]] && userAuthMDMFAILOVER="TRUE"
{ [[ "$upgradeWORKFLOW" != "USER" ]] && [[ "$updateWORKFLOW" != "USER" ]]; } && dialogSoftDeadline
installRestartWorkflowActiveUser # This function includes internal power/storage checks and test mode logic.
elif [[ "$focusDEFER" == "TRUE" ]]; then # No deadlines have passed but a process has told the display to not sleep or the user has enabled Focus or Do Not Disturb.
deferSECONDS="$focusDeferSECONDS"
sendToStatus "Pending: Automatic user focus deferral, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
else # Logically, this is the only time the choice dialog is shown.
dialogRestartOrDefer
if [[ "$choiceINSTALL" == "TRUE" ]]; then
installRestartWorkflowActiveUser # This function includes internal power/storage checks and test mode logic.
else
sendToStatus "Pending: User chose to defer, trying again in $deferSECONDS seconds."
makeLaunchDaemonCalendar
fi
fi
fi
fi
fi
# At this point super is about to exit, so wrap up for different workflow exit modes.
if [[ "$restartVALIDATE" == "TRUE" ]]; then # An update/upgrade is about to restart the computer.
sendToLog "Exit: macOS update/upgrade restart is imminent, super is scheduled to run at next startup."
sendToStatus "Pending: macOS update/upgrade restart is imminent, super is scheduled to run at next startup."
else # The super workflow did not complete a macOS update/upgrade workflow.
if [[ "$installNowOPTION" == "TRUE" ]]; then
notifyInstallNowUpToDate
if [[ "$testModeOPTION" == "TRUE" ]]; then
sendToLog "Test Mode: Pausing $testModeTimeoutSECONDS seconds for the install now up to date notification..."
sleep "$testModeTimeoutSECONDS"
kill -9 "$notifyPID" > /dev/null 2>&1
if [[ "$ibmNotifierVALID" == "TRUE" ]] && [[ "$preferJamfHelperOPTION" != "TRUE" ]]; then
killall -9 "IBM Notifier" "IBM Notifier Popup" > /dev/null 2>&1
else
killall -9 "jamfHelper" > /dev/null 2>&1
fi
fi
fi
# A few bits of cleanup for $onlyDownloadOPTION.
if [[ "$onlyDownloadCOMPLETE" == "TRUE" ]]; then
if [[ "$jamfVERSION" != "FALSE" ]] && [[ "$jamfSERVER" != "FALSE" ]]; then
sendToLog "Status: Submitting updated inventory to Jamf Pro. Use --verbose-mode or check /var/log/jamf.log for more detail..."
if [[ "$verboseModeOPTION" == "TRUE" ]]; then
jamfRESULT=$("$jamfBINARY" recon -verbose 2>&1)
jamfRETURN=$?
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRESULT is:\n$jamfRESULT"
sendToLog "Verbose Mode: Function ${FUNCNAME[0]}: jamfRETURN is: $jamfRETURN"
else
"$jamfBINARY" recon > /dev/null 2>&1
fi
fi
fi
# Logic for $recheckDeferSECONDS.
if [[ -n "$recheckDeferSECONDS" ]]; then
deferSECONDS="$recheckDeferSECONDS"
sendToLog "Recheck deferral should restart super in $deferSECONDS seconds."
sendToStatus "Pending: Recheck deferral should restart super in $deferSECONDS seconds."
makeLaunchDaemonCalendar
else # Recheck deferral is inactive.
sendToLog "Status: Recheck deferral is inactive."
sendToStatus "Inactive: Recheck deferral is inactive."
sendToPending "Inactive."
removeLaunchDaemon
fi
fi
}
mainWorkflow "$@"
cleanExit