10.14.4 Update and T2 Macs

cstout
Contributor III

I'm curious if anyone out there is seeing identical behavior to the post linked below: JN Post - Update required/startup disk

The macOS 10.14.4 update is being requested and installed via a script that's using Apple-recommended means: softwareupdate --install --recommended

On non-T2 Macs the update applies normally. On two T2 Macs I just deployed it to they behaved the same...extremely poor. I got the same behavior as described and pictured in the post above. My 2018 Mac mini (not encrypted) was able to boot up and finish the update after selecting to "choose another startup disk" and then selecting Macintosh HD.

My 2018 MacBook Air, which is encrypted, made me choose the disk, unlock, restart, unlock, and booted to a black screen from which it would not leave. After a hard shut down, I powered it back on and it rebooted three times before giving me the progress bar for the 10.14.4 update.

For making this post more search friendly, the error on initial reboot is:
"A software update required to use this startup disk."

18 REPLIES 18

gachowski
Valued Contributor II

No errors like you, however I have seen three machines freeze and need a hard reboot... but they all working "normally" after that ....

C

cstout
Contributor III

I have a theory that I'm still working through. It may be a conflict between the "jamf reboot" command and "softwareupdate -i -r". Now, since my clients are all higher than 10.13.4 I'm going to change out some logic in the script to utilize softwareupdate --install --restart to facilitate the required reboot.

cstout
Contributor III

That's exactly it. The output below is from softwareupdate directly. My software update script (and I'm sure plenty of other's) calls a reboot after installing updates. The reason being not all updates require a reboot so when one is required and detected the script will warn the user and then proceed to reboot. This process is not usable for 10.14.4 as it must shut down and not restart. Big thank you to Apple for noting this.

To install these updates, your computer must shut down. Your computer will automatically start up to finish installation. Installation will not complete successfully if you choose to restart your computer instead of shutting down. Please call halt(8) or select Shut Down from the Apple menu. To automate the shutdown process with softwareupdate(8), use --restart.

bpavlov
Honored Contributor

m_entholzner
Contributor III
Contributor III

I've seen exactly the same behavior that @cstout mentioned. This occurred in multiple customer environments and even on non-managed machines were affected. So be honest, I don't think this has something to do with restart-commands that are sent either through Jamf or via CLI. We're not using both of them, all of our clients are set to auto-update and are affected anyway.

Edit: additionally, we've seen this on non-T2 Macs too...

bwiessner
Contributor II

I was able to package up the 10.14.4 combo pkg and do a cache policy and then an separate install policy with "Perform authenticated restart on computers with FileVault 2 enabled" - had no issues with T2 macs.

JayDuff
Contributor II

Apple has released articles SN5135 and TP1658, that talk about issues transferring data between 10.14.4 Macs with the T2 chip. Apparently, only a Mac running 10.14.0 - 10.14.3 can be used. I wonder if this problem has the same root cause.

tnielsen
Valued Contributor

I haven't had this problem, so far...

cstout
Contributor III

I confirmed this morning that after removing the script logic to use jamf's binary for a restart the update applies properly on my T2 Macs. I'm working through updating the logic on the updater script but for now simply calling --install --all --restart does everything that's needed.

jameson
Contributor II

I actually been using defer or install script that had worked great - but for T2 chips it does not work and prompt for startup disk and have to do it twice before it works

I am not a script guru, but is there a option to build --restart in so I can try and test

#!/bin/bash
# shellcheck disable=SC2001,SC2004

###
#
#            Name:  install_or_defer.sh
#     Description:  This script, meant to be triggered periodically by a
#                   LaunchDaemon, will prompt users to install Apple system
#                   updates that the IT department has deemed "critical." Users
#                   will have the option to Run Updates or Defer. After a
#                   specified amount of time, the update will be forced. If
#                   updates requiring a restart were found in the update check,
#                   restarts automatically.
#         Authors:  Elliot Jordan and Mario Panighetti
#         Created:  2017-03-09
#   Last Modified:  2019-01-11
#         Version:  2.1
#
###


########################## FILE PATHS AND IDENTIFIERS #########################

# Path to a plist file that is used to store settings locally. Omit ".plist"
# extension.
PLIST="/Library/Preferences/com.elliotjordan.install_or_defer"

# (Optional) Path to a logo that will be used in messaging. Recommend 512px,
# PNG format. If no logo is provided, the App Store icon will be used.
LOGO="/private/var/db/512x512.png"

# The identifier of the LaunchDaemon that is used to call this script, which
# should match the file in the payload/Library/LaunchDaemons folder. Omit
# ".plist" extension.
BUNDLE_ID="com.elliotjordan.install_or_defer"


################################## MESSAGING ##################################

# The messages below use the following dynamic substitutions:
#   - %DEFER_HOURS% will be automatically replaced by the number of hours
#     remaining in the deferral period.
#   - The section in the {{double curly brackets}} will be removed when this
#     message is displayed for the final time before the deferral deadline.
#   - The sections in the <<double comparison operators>> will be removed if a restart
#     is not required for the pending updates.
#   - %UPDATE_MECHANISM% will be automatically replaced depending on macOS
#     version:
#     - macOS 10.13 or lower: "App Store > Updates"
#     - macOS 10.14+: "System Preferences > Software Update"

# The message users will receive when updates are available, shown above the
# "Run Updates" and "Defer" buttons.
MSG_ACT_OR_DEFER_HEADING="Critical updates are available"
MSG_ACT_OR_DEFER="Apple has released critical security updates.  would like you to install them as soon as possible. Please save your work, quit all applications, and click Install button.

{{If now is not a good time, you can click defer and the update message will show up again in 2 hours. If the update has not been installed within 5 days, it will auto install(NOTE: You risk losing unsaved data as update will reboot computer)"

# The message users will receive after the deferral deadline has been reached.
MSG_ACT_HEADING="Please Install Updates Now"
MSG_ACT="Updates will be automatically installed now as maximum of days deferral has passed. Your Mac will restart automatically when this is finished"

# The message users will receive while updates are running in the background.
MSG_UPDATING_HEADING="Running Updates"
MSG_UPDATING="Running system updates in the background. Your Mac will restart automatically when this is finished"


#################################### TIMING ###################################

# Number of seconds between the first script run and the updates being forced.
MAX_DEFERRAL_TIME=$(( 60 * 60 * 24 * 5 )) # (259200 = 3 days)

# When the user clicks "Defer" the next prompt is delayed by this much time.
EACH_DEFER=$(( 60 * 60 * 2 )) # (14400 = 4 hours)

# The number of seconds to wait between displaying the "install updates" message
# and applying updates, then attempting a soft restart.
UPDATE_DELAY=$(( 60 * 2 )) # (120 = 2 minutes)

# The number of seconds to wait between attempting a soft restart and forcing a
# restart.
HARD_RESTART_DELAY=$(( 60 * 2 )) # (120 = 2 minutes)


################################## FUNCTIONS ##################################

# This function takes a number of seconds as input and returns hh:mm:ss format.
# Source: http://stackoverflow.com/a/12199798
# License: CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/)
# Created by: perreal (http://stackoverflow.com/users/390913/perreal)
convert_seconds () {

    ((h=${1}/3600))
    ((m=(${1}%3600)/60))
    ((s=${1}%60))
    printf "%02dh:%02dm:%02ds
" $h $m $s

}

# This function caches all available critical system updates, or exits if no
# critical updates are available.
check_for_updates () {

    echo "Checking for pending system updates..."
    updateCheck=$(softwareupdate --list)

    # Determine whether any critical updates are available, and if any require
    # a restart. If no updates need to be installed, bail out.
    if [[ "$updateCheck" == *"[restart]"* ]]; then
        installWhich="all"
        # Remove "<<" and ">>" but leave the text between
        # (retains restart warnings).
        MSG_ACT_OR_DEFER="$(echo "$MSG_ACT_OR_DEFER" | sed 's/[<<|>>]//g')"
        MSG_ACT="$(echo "$MSG_ACT" | sed 's/[<<|>>]//g')"
        MSG_UPDATING="$(echo "$MSG_UPDATING" | sed 's/[<<|>>]//g')"
    elif [[ "$updateCheck" == *"[recommended]"* ]]; then
        installWhich="recommended"
        # Remove "<<" and ">>" including all the text between
        # (removes restart warnings).
        MSG_ACT_OR_DEFER="$(echo "$MSG_ACT_OR_DEFER" | sed 's/<<.*>>//g')"
        MSG_ACT="$(echo "$MSG_ACT" | sed 's/<<.*>>//g')"
        MSG_UPDATING="$(echo "$MSG_UPDATING" | sed 's/<<.*>>//g')"
    else
        echo "No critical updates available."
        clean_up
        exit_without_updating
    fi

    # Download updates (all updates if a restart is required for any, otherwise
    # just recommended updates).
    echo "Pre-downloading $installWhich system updates..."
    softwareupdate --download --$installWhich

}

# Invoked after the deferral deadline passes, this function displays an
# onscreen message instructing the user to apply updates.
display_act_msg () {

    # Create a jamfHelper script that will be called by a LaunchDaemon.
    cat << EOF > "/private/tmp/$HELPER_SCRIPT"
#!/bin/bash
"$jamfHelper" -windowType "utility" -icon "$LOGO" -title "$MSG_ACT_HEADING" -description "$MSG_ACT"
EOF
    chmod +x "/private/tmp/$HELPER_SCRIPT"

    # Create the LaunchDaemon that we'll use to show the persistent jamfHelper
    # messages.
    cat << EOF > "/private/tmp/${BUNDLE_ID}_helper.plist"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>${BUNDLE_ID}_helper</string>
    <key>Program</key>
    <string>/private/tmp/$HELPER_SCRIPT</string>
    <key>ThrottleInterval</key>
    <integer>10</integer>
</dict>
</plist>
EOF

    # Load the LaunchDaemon to show the jamfHelper message.
    echo "Displaying "run updates" message..."
    killall jamfHelper 2>/dev/null
    launchctl load -w "/private/tmp/${BUNDLE_ID}_helper.plist"

    # After specified delay, apply updates.
    echo "Waiting $(( UPDATE_DELAY / 60 )) minutes before automatically applying updates..."
    sleep "$UPDATE_DELAY"
    echo "$(( UPDATE_DELAY / 60 )) minutes have elapsed since user was prompted to run updates. Triggering updates..."

    run_updates

}

# Displays HUD with updating message and runs all cached updates.
run_updates () {

  echo "Running $installWhich system updates..."
  "$jamfHelper" -windowType "hud" -icon "$LOGO" -title "$MSG_UPDATING_HEADING" -description "$MSG_UPDATING" -lockHUD &
  softwareupdate --install --$installWhich
  echo "Finished running updates."
  killall jamfHelper 2>/dev/null
  clean_up

  # Trigger restart if script ran an update which requires it.
  if [[ "$installWhich" = "all" ]]; then
      trigger_restart
  fi

}

# Clean up plist values and self destruct LaunchDaemon and script.
clean_up () {

    echo "Cleaning up stored plist values..."
    defaults delete "$PLIST" AppleSoftwareUpdatesForcedAfter 2>/dev/null
    defaults delete "$PLIST" AppleSoftwareUpdatesDeferredUntil 2>/dev/null

    echo "Cleaning up main script and LaunchDaemon..."
    mv "/Library/LaunchDaemons/$BUNDLE_ID.plist" "/private/tmp/$BUNDLE_ID.plist"
    mv "$0" "/private/tmp/"

}

# This function immediately attempts a "soft" restart, waits a specified amount
# of time, and then forces a "hard" restart.
trigger_restart () {

    # Immediately attempt a "soft" restart.
    echo "Attempting a "soft" restart..."
    CURRENT_USER=$(/usr/bin/stat -f%Su /dev/console)
    USER_ID=$(id -u "$CURRENT_USER")
    if [[ "$OS_MAJOR" -eq 10 && "$OS_MINOR" -le 9 ]]; then
        LOGINWINDOW_PID=$(pgrep -x -u "$USER_ID" loginwindow)
        launchctl bsexec "$LOGINWINDOW_PID" osascript -e 'tell application "System Events" to restart'
    elif [[ "$OS_MAJOR" -eq 10 && "$OS_MINOR" -gt 9 ]]; then
        launchctl asuser "$USER_ID" osascript -e 'tell application "System Events" to restart'
    fi

    # After specified delay, kill all apps forcibly, which clears the way for
    # an unobstructed restart.
    echo "Waiting $(( HARD_RESTART_DELAY / 60 )) minutes before forcing a "hard" restart..."
    sleep "$HARD_RESTART_DELAY"
    echo "$(( HARD_RESTART_DELAY / 60 )) minutes have elapsed since "soft" restart was attempted. Forcing "hard" restart..."

    USER_PIDS=$(pgrep -u "$USER_ID")
    LOGINWINDOW_PID=$(pgrep -x -u "$USER_ID" loginwindow)
    for PID in $USER_PIDS; do
        # Kill all processes except the loginwindow process.
        if [[ "$PID" -ne "$LOGINWINDOW_PID" ]]; then
            kill -9 "$PID"
        fi
    done
    if [[ "$OS_MAJOR" -eq 10 && "$OS_MINOR" -le 9 ]]; then
        launchctl bsexec "$LOGINWINDOW_PID" osascript -e 'tell application "System Events" to restart'
    elif [[ "$OS_MAJOR" -eq 10 && "$OS_MINOR" -gt 9 ]]; then
        launchctl asuser "$USER_ID" osascript -e 'tell application "System Events" to restart'
    fi
    # Mac should restart now, ending this script and installing updates.

}

# Ends script without applying any system updates.
exit_without_updating () {

  echo "Running jamf recon..."
  "$jamf" recon

  "/bin/echo" "Unloading $BUNDLE_ID LaunchDaemon. Script will end here."
  "/bin/launchctl" unload -w "/private/tmp/$BUNDLE_ID.plist"
  exit 0

}


######################## VALIDATION AND ERROR CHECKING ########################

# Copy all output to the system log for diagnostic purposes.
exec 1> >(logger -s -t "$(basename "$0")") 2>&1
echo "Starting $(basename "$0") script. Performing validation and error checking..."

# Filename we will use for the auto-generated helper script.
HELPER_SCRIPT="$(basename "$0" | sed "s/.sh$//g")_helper.sh"

# Flag variable for catching show-stopping errors.
BAILOUT=false

# Bail out if the jamfHelper doesn't exist.
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
if [[ ! -x "$jamfHelper" ]]; then
    echo "[ERROR] The jamfHelper binary must be present in order to run this script."
    BAILOUT=true
fi

# Bail out if the jamf binary doesn't exist.
PATH="/usr/sbin:/usr/local/bin:$PATH"
jamf=$(which jamf)
if [[ -z $jamf ]]; then
    echo "[ERROR] The jamf binary could not be found."
    BAILOUT=true
fi

# Determine macOS version.
OS_MAJOR=$(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}')
OS_MINOR=$(/usr/bin/sw_vers -productVersion | awk -F . '{print $2}')

# If the macOS version is not 10.12 through 10.14, this script may not work.
# When new versions of macOS are released, this logic should be updated after
# the script has been tested successfully.
if [[ "$OS_MAJOR" -eq 10 && "$OS_MINOR" -lt 12 ]] || [[ "$OS_MAJOR" -lt 10 ]]; then
    echo "[ERROR] This script requires at least macOS 10.12. This Mac has $OS_MAJOR.$OS_MINOR."
    BAILOUT=true
elif [[ "$OS_MAJOR" -gt 10 ]] || [[ "$OS_MINOR" -gt 14 ]]; then
    echo "[ERROR] This script has been tested through macOS 10.14 only. This Mac has $OS_MAJOR.$OS_MINOR."
    BAILOUT=true
else
    if [[ "$OS_MINOR" -lt 14 ]]; then
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER//%UPDATE_MECHANISM%/App Store > Updates}"
        MSG_ACT="${MSG_ACT//%UPDATE_MECHANISM%/App Store > Updates}"
        MSG_UPDATING="${MSG_UPDATING//%UPDATE_MECHANISM%/App Store > Updates}"
    else
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER//%UPDATE_MECHANISM%/System Preferences > Software Update}"
        MSG_ACT="${MSG_ACT//%UPDATE_MECHANISM%/System Preferences > Software Update}"
        MSG_UPDATING="${MSG_UPDATING//%UPDATE_MECHANISM%/System Preferences > Software Update}"
    fi
fi

# We need to be connected to the internet in order to download updates.
ping -q -c 1 208.67.222.222 &>/dev/null
if [[ $? -ne 0 ]]; then
    echo "[ERROR] No connection to the Internet."
    BAILOUT=true
fi

# If FileVault encryption or decryption is in progress, installing updates that
# require a restart can cause problems.
if fdesetup status | grep -q "in progress"; then
    echo "[ERROR] FileVault encryption or decryption is in progress."
    BAILOUT=true
fi

# If any of the errors above are present, bail out of the script now.
if [[ "$BAILOUT" == "true" ]]; then
    START_INTERVAL=$(defaults read /Library/LaunchDaemons/$BUNDLE_ID.plist StartInterval 2>/dev/null)
    if [[ $? -eq 0 ]]; then
        echo "Stopping due to errors, but will try again in $(convert_seconds "$START_INTERVAL")."
    else
        echo "Stopping due to errors."
    fi
    exit 1
else
    echo "Validation and error checking passed. Starting main process..."
fi


################################ MAIN PROCESS #################################

# Validate logo file. If no logo is provided or if the file cannot be found at
# specified path, default to the App Store icon.
if [[ -z "$LOGO" ]] || [[ ! -f "$LOGO" ]]; then
    echo "No logo provided, or no logo exists at specified path. Using App Store icon."
    LOGO="/Applications/App Store.app/Contents/Resources/AppIcon.icns"
fi

# Perform first run tasks, including calculating deadline.
FORCE_DATE=$(defaults read "$PLIST" AppleSoftwareUpdatesForcedAfter 2>/dev/null)
if [[ -z $FORCE_DATE || $FORCE_DATE -gt $(( $(date +%s) + MAX_DEFERRAL_TIME )) ]]; then
    FORCE_DATE=$(( $(date +%s) + MAX_DEFERRAL_TIME ))
    defaults write "$PLIST" AppleSoftwareUpdatesForcedAfter -int $FORCE_DATE
fi

# Calculate how much time remains until deferral deadline.
DEFER_TIME_LEFT=$(( FORCE_DATE - $(date +%s) ))
echo "Deferral deadline: $(date -jf "%s" "+%Y-%m-%d %H:%M:%S" "$FORCE_DATE")"
echo "Time remaining: $(convert_seconds $DEFER_TIME_LEFT)"

# Check for updates, exit if none found, otherwise cache locally and continue.
check_for_updates

# Get the "deferred until" timestamp, if one exists.
DEFERRED_UNTIL=$(defaults read "$PLIST" AppleSoftwareUpdatesDeferredUntil 2>/dev/null)
if [[ -n "$DEFERRED_UNTIL" ]] && (( DEFERRED_UNTIL > $(date +%s) && FORCE_DATE > DEFERRED_UNTIL )); then
    # If the policy ran recently and was deferred, we need to respect that
    # "defer until" timestamp, as long as it is earlier than the deferral
    # deadline.
    echo "The next prompt is deferred until after $(date -jf "%s" "+%Y-%m-%d %H:%M:%S" "$DEFERRED_UNTIL")."
    exit 0
fi

# Make a note of the time before displaying the prompt.
PROMPT_START=$(date +%s)

# If defer time remains, display the prompt. If not, install and restart.
if (( DEFER_TIME_LEFT > 0 )); then

    # Substitute the correct number of hours remaining.
    if (( DEFER_TIME_LEFT > 7200 )); then
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER//%DEFER_HOURS%/$(( DEFER_TIME_LEFT / 3600 ))}"
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER// 1 hours/ 1 hour}"
    elif (( DEFER_TIME_LEFT > 60 )); then
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER//%DEFER_HOURS% hours/$(( DEFER_TIME_LEFT / 60 )) minutes}"
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER// 1 minutes/ 1 minute}"
    else
        MSG_ACT_OR_DEFER="${MSG_ACT_OR_DEFER//after %DEFER_HOURS% hours/very soon}"
    fi

    # Determine whether to include the "you may defer" wording.
    if (( EACH_DEFER > DEFER_TIME_LEFT )); then
        # Remove "{{" and "}}" including all the text between.
        MSG_ACT_OR_DEFER="$(echo "$MSG_ACT_OR_DEFER" | sed 's/{{.*}}//g')"
    else
        # Just remove "{{" and "}}" but leave the text between.
        MSG_ACT_OR_DEFER="$(echo "$MSG_ACT_OR_DEFER" | sed 's/[{{|}}]//g')"
    fi

    # Show the install/defer prompt.
    echo "Prompting to install updates now or defer..."
    PROMPT=$("$jamfHelper" -windowType "utility" -icon "$LOGO" -title "$MSG_ACT_OR_DEFER_HEADING" -description "$MSG_ACT_OR_DEFER" -button1 "Install" -button2 "Defer" -defaultButton 2 -timeout 3600 -startlaunchd 2>/dev/null)
    JAMFHELPER_PID=$!

    # Make a note of the amount of time the prompt was shown onscreen.
    PROMPT_END=$(date +%s)
    PROMPT_ELAPSED_SEC=$(( PROMPT_END - PROMPT_START ))

    # Generate a duration string that will be used in log output.
    if [[ -n $PROMPT_ELAPSED_SEC && $PROMPT_ELAPSED_SEC -eq 0 ]]; then
        PROMPT_ELAPSED_STR="immediately"
    elif [[ -n $PROMPT_ELAPSED_SEC ]]; then
        PROMPT_ELAPSED_STR="after $(convert_seconds "$PROMPT_ELAPSED_SEC")"
    elif [[ -z $PROMPT_ELAPSED_SEC ]]; then
        PROMPT_ELAPSED_STR="after an unknown amount of time"
        echo "[WARNING] Unable to determine elapsed time between prompt and action."
    fi

    # For reference, here is a list of the possible jamfHelper return codes:
    # https://gist.github.com/homebysix/18c1a07a284089e7f279#file-jamfhelper_help-txt-L72-L84

    # Take action based on the return code of the jamfHelper.
    if [[ -n $PROMPT && $PROMPT_ELAPSED_SEC -eq 0 ]]; then
        # Kill the jamfHelper prompt.
        kill -9 $JAMFHELPER_PID
        echo "[ERROR] jamfHelper returned code $PROMPT $PROMPT_ELAPSED_STR. It's unlikely that the user responded that quickly."
        exit 1
    elif [[ -n $PROMPT && $DEFER_TIME_LEFT -gt 0 && $PROMPT -eq 0 ]]; then
        echo "User clicked Install Updates $PROMPT_ELAPSED_STR."
        defaults delete "$PLIST" AppleSoftwareUpdatesDeferredUntil 2>/dev/null
        run_updates
    elif [[ -n $PROMPT && $DEFER_TIME_LEFT -gt 0 && $PROMPT -eq 1 ]]; then
        # Kill the jamfHelper prompt.
        kill -9 $JAMFHELPER_PID
        echo "[ERROR] jamfHelper was not able to launch $PROMPT_ELAPSED_STR."
        exit 1
    elif [[ -n $PROMPT && $DEFER_TIME_LEFT -gt 0 && $PROMPT -eq 2 ]]; then
        echo "User clicked Defer $PROMPT_ELAPSED_STR."
        NEXT_PROMPT=$(( $(date +%s) + EACH_DEFER ))
        defaults write "$PLIST" AppleSoftwareUpdatesDeferredUntil -int "$NEXT_PROMPT"
        echo "Next prompt will appear after $(date -jf "%s" "+%Y-%m-%d %H:%M:%S" "$NEXT_PROMPT")."
    elif [[ -n $PROMPT && $DEFER_TIME_LEFT -gt 0 && $PROMPT -eq 239 ]]; then
        echo "User deferred by exiting jamfHelper $PROMPT_ELAPSED_STR."
        NEXT_PROMPT=$(( $(date +%s) + EACH_DEFER ))
        defaults write "$PLIST" AppleSoftwareUpdatesDeferredUntil -int "$NEXT_PROMPT"
        echo "Next prompt will appear after $(date -jf "%s" "+%Y-%m-%d %H:%M:%S" "$NEXT_PROMPT")."
    elif [[ -n $PROMPT && $DEFER_TIME_LEFT -gt 0 && $PROMPT -gt 2 ]]; then
        # Kill the jamfHelper prompt.
        kill -9 $JAMFHELPER_PID
        echo "[ERROR] jamfHelper produced an unexpected value (code $PROMPT) $PROMPT_ELAPSED_STR."
        exit 1
    elif [[ -z $PROMPT ]]; then # $PROMPT is not defined
        # Kill the jamfHelper prompt.
        kill -9 $JAMFHELPER_PID
        echo "[ERROR] jamfHelper returned no value $PROMPT_ELAPSED_STR. Install Updates/Defer response was not captured. This may be because the user logged out without clicking Run Updates/Defer."
        exit 1
    else
        # Kill the jamfHelper prompt.
        kill -9 $JAMFHELPER_PID
        echo "[ERROR] Something went wrong. Check the jamfHelper return code ($PROMPT) and prompt elapsed seconds ($PROMPT_ELAPSED_SEC) for further information."
        exit 1
    fi

else
    # If no deferral time remains, force installation of updates now.
    echo "No deferral time remains."
    display_act_msg
fi

exit 0

mario
New Contributor II

@jameson: Install or Defer was updated last month (version 2.1.2 or later) to account for BridgeOS updates requiring a system shutdown as opposed to a restart. Give it another try!

tlarkin
Honored Contributor

We have a ton of T2 Macbook Pros running 10.14.4 and only ran into a few minor issues. The biggest issue is the amount of time it takes to run SWU. I am just invoking softwareupdate -iRa

carlo_anselmi
Contributor III

@tlarkin is that sent from "Execute Command" within a policy targetting a smart group with T2 that need to be updated or are you using a more complex script with deferral? There are several versions around but could not find one 100% reliable
Thank you!

tlarkin
Honored Contributor

@carlo.anselmi I am essentially using a script in all enrollment and bootstrapping scenarios and using the execute command payload in a policy otherwise. In my experience you get the best results when the user actually does the clicking in the UI versus any code and/or automation though.

There have been improvements as of 10.14.4 though, and hopefully Apple will make them better.

Pagigostra
New Contributor II

@cstout Is there any way to give a message using update -iRa to notify the user how long they have before a restart happens? I am only having issues with my script when upgrading between minor versions on the T2 chipsets.

Also, I was curious if you did softwareupdate -d would this download all available updates? Would running softwareupdate -iRa install from the /Library/Updates or would you have to specifically call out

sanbornc
New Contributor III

Hello Everyone! Im trying to do something similar to this script, but way more simplified. I have a plist that runs once a months that calls this script and runs it. The problem Im having is that the "Script failed to match on any string"" element is the only thing that gets written to my log file. Even when software update --list returns "No new software available." Any idea what Im doing wrong? It should be writing "No new software available" to the log when it gets that return, not "Script failed to match on any string"

#!/bin/sh
#make sure log directory exists
if [ ! -d "/Library/Application Support/OCIO" ]; then
                mkdir "/Library/Application Support/OCIO"
fi
#set log file location to log outcome of operation and write the current time
logfile="/Library/Application Support/OCIO/forcereboot_`date +%Y-%m-%d`.log"
echo `date +%H:%M:%S` >> $logfile
#Check for updates
updateCheck=$(softwareupdate --list)
if [[ "$updateCheck" == *"Software Update found the following new or updated software:"* ]]; then
                echo "Installing Updates." >> $logfile
                /usr/local/jamf/bin/jamf displayMessage -message "Your Mac will reboot in 5 minutes to install necessary system updates.  Please save all work and quit applications now."
                sleep 300
                softwareupdate -iaR >> $logfile
elif [[ "$updateCheck" == *"No new software available."* ]]; then
                echo "No updates found that require restart" >> $logfile
                exit 0
else
                echo "Script failed to match on any string" >> $logfile
                exit 0
fi

ChrisCox
New Contributor III

@sanbornc The softwareupdate command outputs some of its feedback to console using STDERR even when it does not actually encounter an error, so you will need to redirect STDERR to follow STDOUT. I.e. change this line:

updateCheck=$(softwareupdate --list)

to this line:

updateCheck=$(softwareupdate --list 2>&1)

donmontalvo
Esteemed Contributor II

Confirmed with Apple that either of these (without sudo if run through Jamf Pro):

sudo softwareupdate --install --all --restart --force
sudo softwareupdate -iaRf

...will handle shutdown or reboot, as required by the pending updates, including bridgeOS updates.

Was pointed to man softwareupdate:

    -R | --restart
        Automatically restart (or shut down) if required to complete installation. If the user invok-
        ing this tool is logged in then macOS will attempt to quit all applications, logout, and
        restart. If the user is not logged in, macOS will trigger a forced reboot if necessary. If
        you wish to always perform a forced reboot, pass -f (--force).

Jamf's built in function seems to handle shutdown vs reboot correctly too...though not many users had bridgeOS updates pending.

--
https://donmontalvo.com