Posted on 09-20-2012 11:36 AM
So I stole the script from https://jamfnation.jamfsoftware.com/featureRequest.html?id=751 and tweaked the wording a bit.
#!/bin/sh
HELPER=`/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png -heading "Software Updates are Available" -description "Would you like to install updates? System may need to be rebooted." -button2 "Install" -button1 "Cancel" -cancelButton "1"`
echo "jamf helper result was $HELPER";
if [ "$HELPER" == "2" ]; then
/usr/sbin/jamf policy -trigger SWUJH
exit 0
else
echo "user chose No";
exit 1
fi
I also tweaked the cancel button due to when I was using it in it's default form everything returned "user chose No" and it wouldn't run the trigger.
Now when I select install, the box disappears then immediately reappears and I have to choose install again, and then teh box stays gone, but the trigger never starts the updates.
Solved! Go to Solution.
Posted on 09-20-2012 04:31 PM
Hey John, I went a few steps further and added some logic into the script to count how many times the user chooses no before the script runs automatically anyway. To me, it gives the user a greater sense of control even if only perceived.
#!/bin/sh
fRunUpdates ()
{
## Once the user OKs the updates or they run automatically, reset the timer to 5
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -lockhud -heading 'ISD is updating software on your computer' -description 'We are now updating your Mac System software. These updates should not take longer than 30 to 45 minutes depending on how many updates your Mac needs. If you see this screen for more than 45 minutes please call our Service Desk at X4949. Please do not turn off this computer. This message will go away when updates are complete.' -icon /Library/Application Support/JAMF/EndUserSupport/AGRose.icns > /dev/null 2>&1 &
## We'll need the pid of jamfHelper to kill it once the updates are complete
JHPID=`echo "$!"`
/usr/sbin/jamf policy -trigger SoftwareUpdate &
## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
SUPID=`echo "$!"`
wait $SUPID
## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away
kill -s KILL $JHPID
exit 0
}
######### Set variables for the script ############
########## Get the group membership for the client #####################
## Get MAC Address using networksetup
MAC=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )
## Use the JSS API to get the Mac's group memberships
JSSGroups=$( curl -s -u username:password https://<casper server>:8443/JSSResource/computers/macaddress/$MAC
| xpath //computer/groups_accounts/computer_group_memberships[1]
| sed -e 's/<computer_group_memberships>//g;s/</computer_group_memberships>//g;s/<group>//g;s/</group>/
/g' )
## Set up the software update time if it does not exist already
if [ ! -e /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt ]; then
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
fi
## Get the timer value
Timer=`cat /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt`
## Get the currently logged in user, if any. Also check for updates that require a restart and ones that do not.
UpdatesNoRestart=`softwareupdate -l | grep recommended | grep -v restart`
RestartRequired=`softwareupdate -l | grep restart | grep -v '*' | cut -d , -f 1`
LoggedInUser=`who | grep console | awk '{print $1}'`
################ End Variable Set ################
## Use echo and grep to find known-core (non system) software update groups. If these groups are found, run these installers silently since no restarts are required for these updates. Use an array to see which updates we take account of. The names of the array elements are also trigger names for each update. This way when there's a new software package to keep updated, we add the trigger name into the array, and the update policy to the JSS. Casper does the rest
NonSysCore=( 'SoftwareUplift-FlashPlayer' 'SoftwareUplift-Flip4Mac' 'SoftwareUplift-FontNuke' 'SoftwareUplift-PrintWindow' 'SoftwareUplift-MicrosoftOffice' 'SoftwareUplift-MicrosoftOutlook' )
for (( i = 0; i < ${#NonSysCore[@]}; i++ ))
do
CheckUpdate=`echo "$JSSGroups" | grep "${NonSysCore[$i]}"`
if [ "$CheckUpdate" != "" ]; then
jamf policy -trigger "${NonSysCore[$i]}"
fi
done
## If there are no system updates, quit
if [ "$UpdatesNoRestart" == "" -a "$RestartRequired" == "" ]; then
echo "No updates at this time"
exit 0
fi
## If we get to this point and beyond, there are updates.
## if there is no one logged in, just run the updates
if [ "$LoggedInUser" == "" ]; then
/usr/sbin/jamf policy -trigger SoftwareUpdate
else
## someone is logged in. prompt if any updates require a restart ONLY IF the update timer has not reached zero
if [ "$RestartRequired" != "" ]; then
## If someone is logged in and they have not canceled 5 times already, prompt them to install updates that require a restart and state how many more times they can press 'cancel' before updates run automatically.
if [ $Timer -gt 0 ]; then
HELPER=`/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application Support/JAMF/EndUserSupport/AGRose.icns -heading "AG ISD Approved Software Updates are Available for Your Mac" -description "These updates will require you to restart your Mac. If you would like to install these now, click 'Install Updates.' If you would not like to install now, click 'Cancel Updates.' You may choose not to install updates $Timer more times before this computer will automatically install them. These updates require a restart of your Mac: $RestartRequired" -button1 "Install Updates" -button2 "Cancel Updates" -cancelButton "2" -defaultButton 2 -timeout 60`
echo "jamf helper result was $HELPER";
## If they click Install Updates then run the updates
if [ "$HELPER" == "0" ]; then
fRunUpdates
else
## If no, then reduce the timer by 1. The script will run again the next day
let CurrTimer=$Timer-1
echo "user chose No"
echo "$CurrTimer" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
exit 1
fi
else
## If Timer is already 0, run the updates automatically, the user has been warned!
fRunUpdates
fi
fi
fi
## Install updates that do not require a restart
if [ "$UpdatesNoRestart" != "" ]; then
/usr/sbin/jamf policy -trigger SoftwareUpdate
fi
Posted on 03-18-2014 01:34 PM
yeah JH has a character/line limit. not sure what it is, though it looks like 6 lines is the Max. Might switch to using cocoaDialog
Posted on 03-19-2014 11:54 AM
Okay so some odd behavior with your script. When logged in as a local admin, I'm not given the option to install or cancel, just the JH popup about installing software. This is the following I get in the log:
Script result: Found 1 nodes: -- NODE -- cat: /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt: No such file or directory 2014-03-19 12:43:02.314 softwareupdate[1089:4403] PackageKit: Missing bundle path, skipping: 2014-03-19 12:43:17.283 softwareupdate[1103:2a07] PackageKit: Missing bundle path, skipping: /Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 76: [: -gt: unary operator expected Checking for policies triggered by "SoftwareUpdate"... No policies were found for the "SoftwareUpdate" trigger. /Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 20: kill: (1128) - No such process
Any ideas (other than I forgot to change the SoftwareUpdate trigger to softwareupdate -ai)?
Posted on 03-19-2014 12:31 PM
yes
/Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 76: [: -gt: unary operator expected
That error means that the '$Timer' variable that holds the value in the .SoftwareUpdateTime.txt file (5, 4, 3, 2, 1, or 0 in my script) did not evaluate to a integer -- it either was nothing or a string, in which case the comparison would fail ( -gt is only for integer comparisons). It most likely did not evaulate to anything - NULL or a blank, or something. If that comparison fails, the entire condition won't be met and it will default to the next item in the if..then..else statement which is to run the fRunUpdates function that shows the JH hud window about running updates.
Make sure you are starting your script off by creating the timer file if it's not already there:
if [ ! -e /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt ]; then
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
fi
and then writing the new value (in my case it is current value - 1) to the same file if the user chooses not to run updates. Make sure to use a > to overwrite the value already stored there, not a >> to append:
let CurrTimer=$Timer-1
echo "user chose No"
echo "$CurrTimer" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
Same goes for when the script actually DOES run updates. You need to reset that timer file for the next round of updates, whenever that is:
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
Posted on 03-19-2014 12:38 PM
derp... you'll want to make sure the line where your .SoftwareUpdateTimer.txt file is checked for a value is created before you try to get a value stored in there. On a first run of the script above it will fail to get a value -- thats the error you are seeing. On subsequent runs there will be a file created and you won't see that error any longer... I just love logic errors. Script above has been fixed.
Posted on 03-25-2014 08:34 AM
let CurrTimer=$Timer-1
echo "user chose No"
echo "$CurrTimer" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
Seems to already have been set in the script I have. Is there an issue with that?
I also noticed what @johnnasset mentioned regarding the go ahead to install updates. Also is there a way to make more room in the helper windows for "reboot" updates? Or is this truly a limitation. I also cut down on text in buttons. This helped greatly with look and feel.
I also added the creation of the SoftwareUpdateTime.txt file. I should have mentioned that. After my last edit things seemed to be better. I just figured I would have the script create the file other than having another policy to push it out. I was not aware that this needed to happen for other reasons.
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
Was also already there and correct in my script. I think you had that right before. does that need to be places elsewhere?
Thank you for the help.
Posted on 03-25-2014 10:25 AM
Look at the way Robotcloud is using the same type of idea. I would love to see there script. http://robotcloud.screenstepslive.com/s/2459/m/5322/l/87479-install-all-updates-policy
Posted on 03-25-2014 10:37 AM
And look at their innovative Self Service portal!! This seems familiar:
http://robotcloud.screenstepslive.com/s/2459/m/5322/l/76738-self-service
Posted on 03-25-2014 11:07 AM
Its no secret RobotCloud is using the Casper Suite. They've been using it for years, but they've done some nice stuff around pulling data via the API and other methods into a completely custom UI. Looks nothing like the JSS, but the data is all from the JSS.
@dvasquez - as far as I know, there is no way to get around the line limitation in jamfHelper. I ran into it several times myself when doing this kind of thing a year or so back. Its partly why I chose to do my windowing in cocoaDialog. jamfHelper still has some useful modes, like fullscreen and hud, but for general user facing window stuff, I stick with cocoaDialog.
Posted on 04-28-2014 12:49 PM
@acdesigntech][/url Hello. I am still seeing in test where when new updates are available the Helper prompt that lists updates that require an update does not come up. Instead it displays right away the "Installing Updates prompt". I made the adjustments you mentioned above. The logs do not mention anything specific or mentions successful completion. Also if I manually run the script and cancel at first sign of no list prompt then manually run it again it works correctly, I see the prompt with the list of updates. I also check the .SoftwareUpdateTimer.txt and it has been reset. Any ideas why this might still be happening. It seems to me that the script when run triggered by policy is not properly reading or locating the .SoftwareUpdateTimer.txt files when it first runs. I could be wrong though, of course.
Here is what I have:
## Once the user OKs the updates or they run automatically, reset the timer to 5 echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
and
if [ ! -e /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt ]; then
echo "5" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt
fi
and
let CurrTimer=$Timer-1 echo "user chose No" echo "$CurrTimer" > /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt exit 1 fi
I also had cut off text but shortened the text lines.
Thank you for your help.
Posted on 04-28-2014 01:25 PM
dominic, if you post your script I might be able to help you. If you copy and paste my script above into your text editor and save it up to the JSS, it should work via policy. Make sure you check your line breaks and formatting though when copy/pasting. Copying from a website sometimes screws with my formatting.
Posted on 04-28-2014 01:31 PM
@acdesigntech I should check that. It is essentially your script edited to fit. I did not make any changes to major parts of your script. Thank you.
Posted on 05-01-2014 08:26 AM
I hope that I am just doing something stupid, and this is a quick fix here. I just started testing this out, and I really like where this could go. My first time trying this on a machine running 10.9.1, I get the following error:
Software Updates are available, and a user is logged in. Moving to initial dialog... There are some non reboot updates available. Showing selection screen to user User chose to Cancel. Exiting...
Tried this a few times, rebooted, tried different user, but I don't see a selection box pop up, and I am certainly not hitting cancel.
Yup, it was pretty stupid. Answer was here:
The only real requirements are the 3.0 beta 7 version of cocoaDialog to be on the Mac, and a Mac with at least one available Software Update to run it against. The script exits silently if no updates are available,
Posted on 05-15-2014 05:12 PM
Ladies and gentlemenssss.
junki is here..
https://jamfnation.jamfsoftware.com/discussion.html?id=10636
Posted on 08-14-2014 06:15 AM
Is there's a way to tweak this script to look for a policy with manual trigger instead of looking for an updates and let user postpone installation 3 times?
Posted on 06-15-2015 09:36 AM
I am fairly new to Casper to take it easy on me. We would like to automate the deployment of OS updates. All of the scripts in this thread great. We are running on to the following problem for the scripts we have tried so far. The message is No policies were found for the "runsoftwareupdate" trigger.
What does this mean? Do we need to create a second policy with a custom trigger value of "runsoftwareupdate"?
Posted on 06-15-2015 09:50 AM
Do we need to create a second policy with a custom trigger value of "runsoftwareupdate"?
Yes, anytime you do something like sudo jamf policy -trigger <sometriggername> The sometriggername must be set up as a policy with a custom trigger (referred to as "events" in Casper Suite 9). Generally this is enabled in place of any other triggers, like Recurring check-in, login, etc, but its possible to check more than one trigger.
Posted on 06-15-2015 09:54 AM
Yes, a trigger would be the custom trigger for another policy. As to what that other policy would contain, I'm not sure. As you can tell from this thread, there are a lot of different ways people are approaching this so I'm not sure what that particular script you are using had in mind specifically.
Posted on 06-15-2015 10:15 AM
@bpavlov Thank you. This helped. I don't have many computers in our testing environment that need updates still but I just ran the two policies on a computer and the update didn't install even though an update was found. Is this normal?
Posted on 06-15-2015 10:53 AM
@ksanborn There are probably like 15 different scripts on this thread. I'm not sure if anyone can help you troubleshoot unless you let us know which script you are using and perhaps even post the script itself, even if its basically identical to what is already here, and some details on how you have this setup in your JSS. Otherwise, its going to be pretty hard to help out.
Posted on 06-15-2015 11:34 AM
The last version I posted in this crazy long thread should still work, though would need the syntax changed to be Casper 9 compatible on the line that calls the second policy.
If you are still stuck I can give you a little bit of help, though I don't know whose script and which iteration you are using :)
Posted on 06-16-2015 04:03 AM
All, thank you for your assistance. Everything is working now. My only question is, whether or not we can add a company logo to the message box? We already have the logo on the Macs. You guys are great. I did the following:
1.) I copied and pasted the script below in to a script in to JSS.
use strict;
my $AVAILABLEUPDATES="";
my $CHANCESTOUPDATE=3;
my $COUNTFILE='/etc/SUScount.txt';
my $UPDATECOUNT=0;
$AVAILABLEUPDATES=/usr/sbin/softwareupdate --list
;
chomp $AVAILABLEUPDATES;
printf "available updates is %s ", "$AVAILABLEUPDATES";
unless (-e $COUNTFILE){
system "/bin/echo 0 > $COUNTFILE";
}
$UPDATECOUNT=/bin/cat $COUNTFILE
;
printf "update count is $UPDATECOUNT
";
if ($AVAILABLEUPDATES=~/*/){
printf "there are updates available ";
if ($AVAILABLEUPDATES=~/(restart)|(shutsdown)/){
printf "updates need a restart ";
my $LOGGEDINUSER='';
$LOGGEDINUSER=/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1
;
chomp $LOGGEDINUSER;
printf "value of logged in user is $LOGGEDINUSER.. ";
if ($LOGGEDINUSER=~/[a-zA-Z]/) {
printf "as there is a logged in user checking count ";
my $CHANCESLEFT=$CHANCESTOUPDATE - $UPDATECOUNT; printf "Chances left is $CHANCESLEFT ";
if ($CHANCESLEFT <= 0) { printf "No chances left installing updates and will reboot.. "; system '/usr/sbin/jamf displayMessage -message "Your computer is installing updates and will reboot shortly."'; system "/usr/sbin/jamf policy -trigger runsoftwareupdate"; system "/bin/echo 0 > $COUNTFILE"; exit 0; }
else { printf "$CHANCESLEFT chances left... checking whether ok to restart "; my $ATTEMPT = $UPDATECOUNT 1; my $FINAL = $CHANCESTOUPDATE 1;
my $COMMAND= "'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper' -startlaunchd -windowType utility -icon '/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png' -heading "Software Updates are Available for Your Computer" -description "This is installation attempt $ATTEMPT of $CHANCESTOUPDATE. On attempt $FINAL, updates will install automatically and your machine will restart." -button1 "Yes" -button2 "Defer" -cancelButton "2"";
my $RESPONSE = ""; $RESPONSE=system $COMMAND;
if ($RESPONSE eq "0") {
printf "
User said YES to Updates
";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
} else {
printf "
User said NO to Updates update count is $UPDATECOUNT
";
$UPDATECOUNT=$UPDATECOUNT + 1;
system "/bin/echo $UPDATECOUNT > $COUNTFILE";
printf "
Update count is now $UPDATECOUNT
";
exit 0;
}
}
} else {
printf "no logged in user so ok to run updates
";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
}
else {
printf "no restart required
";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
}
else {
printf "there are no updates available
";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
2.) I created two policies. One policy runs at trigger Reoccurring Check-in, Execution Frequency Once per Month, and with a scope of smart group that contains all Macs that need updates.
3.) The second policy is called RunSoftwareUpdate and has the below set with a trigger of custom runsoftwareupate
Posted on 06-16-2015 08:01 AM
You can choose the icon to use with the -icon option when calling jamfHelper, or you can replace the icon included with jamf helper.
jamfHelper options can be viewed with this command:
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
Posted on 09-30-2015 08:24 PM
Not to bring alive an old thread, but since it was so helpful, I thought I would add something I added to make it work in an environment that has an internal SUS.
function check_corp {
CORP_URL="internalcompanyurl.com"
check_corp="False"
ping=`host -W .5 $CORP_URL`
# If the ping fails - check_corp="False"
[[ ! -z $ping ]] && check_corp="True"
# Check if we are using a test
[[ -n "$1" ]] && check_corp="$1"
}
check_corp
function reset_sus_url {
## Reset the SUS URL to whatever was being used prior to the script running.
if [ "$SUS_URL_SET" == "1" ]; then
/usr/sbin/softwareupdate --set-catalog $SUS_URL
else
/usr/sbin/softwareupdate --clear-catalog
fi
}
internal_sus_url="http://yoursus.company.com:8088/index.sucatalog"
## Check to see what SUS URL is currently being used and set a variable to call later.
SUS_URL=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate CatalogURL | grep $internal_sus_url)
if [ -z "$SUS_URL" ]; then
echo "Using Apple Server"
SUS_URL_SET=""
else
echo "Using Mac Server"
SUS_URL_SET="1"
fi
## If connected ensure that VPN is not active
if [ $check_corp == "True" ]; then
vpn_status="$(ifconfig -lu | grep tun0 )"
if [ ! -z "$vpn_status" ]; then
echo "Currently Connected to VPN"
reset_sus_url
exit 100
else
/usr/sbin/softwareupdate --set-catalog $internal_sus_url
fi
else
/usr/sbin/softwareupdate --clear-catalog
fi
Posted on 02-08-2016 07:19 AM
Late to the party, but here is what I've written to handle this. It downloads the updates before it does anything to interact with the user. It then notifies the user updates are about to be installed and waits for acknowledgement before proceeding, so the user doesn't reboot in the middle of it. Once installed, if no reboot is required it notifies the user that no reboot is required and exits, so that they may reboot as desired without causing problems. If a reboot is required, it counts down 15 minutes before doing so,giving three prompts along the way, every 5 minutes. Each one says "reboot required, will happen automatically in N minutes" and has two buttons. "Reboot now" and "OK". If the user is logged in and the screensaver is active, indicating they are AFK, the script holds in a loop until the user is available to acknowledge. This prevents users screaming because they didn't save a document they had been working on all morning. If no user is logged in, the updates install and reboot automatically if needed.
The only thing I cannot get to work is the authenticated restart for encrypted volumes. I've tried the fde* command directly and tried to kick off a policy which has that configured. Neither ever boots back into the OS, which leaves the system at the filevault login with no way to remotely access it for further updates/inventory. Suggestions on that front welcome.
# Written by Paul Dickson ## Note: JAMF policy #350 is a policy that reboots with filevault authentication touch /tmp/su.log chmod 777 /tmp/su.log b=
softwareupdate -al | grep "found the following" | cut -d " " -f 3-5
# If updates were found, notify the user and begin install. if [ "$b" == "found the following" ]; then # Marking updates as found for later script processing actions found="yes" # Caching/downloading updates before installation attempt softwareupdate -ad # Figure out if any of the updates require a restart softwareupdate -al | grep "[restart]" restart=$? osascript << eof -- Look to see if finder is running. If it is then a user is logged in, so we will notify them about what is going on. if application "Finder" is running and application "ScreenSaverEngine" is not running then tell application "Finder" activate with timeout of 301 seconds display dialog "System updates are now being installed. Please DO NOT shutdown or reboot until notified that the update process is complete." buttons {"Understood"} giving up after 300 end timeout end tell end if eof # Installing all updates softwareupdate -ai fi # If updates were found this will run the jamf policy to update inventory on the server so that the server info on the computer reflects the new updates. if [ "$found" == "yes" ]; then /usr/local/bin/jamf recon; fi if [ "$restart" == "0" ]; then # A restart is required to finish updates. Notify user. osascript << eof -- Look to see if finder is running. If it is then a user is logged in, so we will notify them about what is going on. set a to 1 repeat until a = 0 if application "Finder" is running and application "ScreenSaverEngine" is running then set a to 1 --display dialog "1" do shell script "echo 'repeat 1 waiting' >> /tmp/su.log" delay 5 else set a to 0 --display dialog "0" do shell script "echo 'repeat 1 proceeding' >> /tmp/su.log" end if end repeat if application "Finder" is running then do shell script "echo 'Finder running 1' >> /tmp/su.log" tell application "Finder" activate with timeout of 86400 seconds set question to display dialog "A reboot will be performed in 15 minutes to complete system updates. Please save your work and exit all applications" buttons {"OK", "Reboot now!"} default button 1 giving up after 70000 set answer to button returned of question if answer is equal to "Reboot Now!" then --tell application "Finder" to restart do shell script "jamf policy -id 350" else --delay 5 delay 600 if application "Finder" is running and application "ScreenSaverEngine" is not running then do shell script "-n echo 'Finder running 2' >> /tmp/su.log" tell application "Finder" activate with timeout of 86399 seconds -- This dialog will only wait for user confirmation for 60 seconds before continuing. set question to display dialog "A reboot will be performed in 5 minutes to complete system updates. Please save your work and exit all applications" buttons {"OK", "Reboot now!"} default button 1 giving up after 60 set answer to button returned of question if answer is equal to "Reboot Now!" then --tell application "Finder" to restart do shell script "jamf policy -id 350" else --delay 5 delay 240 if application "Finder" is running and application "ScreenSaverEngine" is not running then do shell script "-n echo 'Finder running 3' >> /tmp/su.log" tell application "Finder" activate with timeout of 86398 seconds -- This dialog will only wait for user confirmation for 60 seconds before continuing. set question to display dialog "A reboot will be performed in 1 minute to complete system updates." buttons {"OK", "Reboot now!"} default button 1 giving up after 60 set answer to button returned of question end timeout end tell if answer is equal to "Reboot Now!" then --tell application "Finder" to restart do shell script "jamf policy -id 350" else --delay 5 delay 60 if application "Finder" is running and application "ScreenSaverEngine" is not running then do shell script "-n echo 'Tell Finder Restart 1' >> /tmp/su.log" --tell application "Finder" to restart do shell script "jamf policy -id 350" end if end if end if end if end timeout end tell end if end if end timeout end tell end if eof echo 'jamf policy -id 350' >> /tmp/su.log jamf policy -id 350 # Checking to see if updates were found. As dictated by being an "elif", This only happens if a reboot isn't required but updates were found. This lets them know a reboot is not required. else if [ "$b" == "found the following" ]; then osascript << eof if application "Finder" is running then tell application "Finder" activate with timeout of 3601 seconds display dialog "The software update process is now complete. No reboot is required" buttons {"OK"} default button 1 giving up after 3600 end timeout end tell end if eof fi fi exit 0
Posted on 02-23-2016 05:07 PM
@MrP Have you tried
do shell script "jamf policy -id 350 &"
to kick off your authenticated restart? I've had luck in the past shifting policy trigger to the background when they are problematic in scripts.
Posted on 07-14-2016 05:25 PM
@mm2270 running your Selectable_SoftwareUpdate.sh script on 10.11.5 comes back with syntax errors:
selectable_SoftwareUpdate.sh: line 461: syntax error near unexpected token `<'
selectable_SoftwareUpdate.sh: line 461: `done < <(echo "${readSWUs}")'
Any updates on this script?
Posted on 07-15-2016 05:34 AM
Hi @dmw3 Can i ask how you're running the script? From the error you posted, I think you may be calling the script in Terminal by doing something like sh /path/to/Selectable_SoftwareUpdate.sh
If so, the problem with doing that is by specifying sh
up front, you're overriding the shell interpreter for the script, which is /bin/bash, not /bin/sh. This script uses some bash specific items in it, namely process substitution, which is what the error you posted is showing.
If you were running it that way, try by making the script executable first and then running it by just putting in the path to the script. It should work that way I think. That being said, I'm not sure if I've tried running this script on 10.11.5, so I'll work on that later today to see if I see any similar errors or other issues with it.
Posted on 07-15-2016 02:49 PM
crazy questions @mm2270 How much work would it be to change your script to not include check boxes? Make more of a "here's what's available to install" in a list. I've taken a quick look at the code, but curious on your thoughts.
Thanks!
Matt
Posted on 07-15-2016 03:27 PM
@mbezzo Not crazy. Instead of removing the checkboxes, how about having them all checked but greyed out/disabled instead? Something like this?
Another option I thought of just now would be to have all the non-reboot updates checked and disabled, so the user can't refuse installs of those, but have the Reboot Required ones optional. It can be either not checked but check-able, like so:
Or, checked by default, but enabled for the user to uncheck it if they needed or wanted to.
All these options are possible, because cocoaDialog supports checked and disabled arrays that you can pass to it to tell it which checkbox items in the index should be set those ways. (Its why I continue to love the product and wish it would get some love to keep it viable)
Note that although I just whipped this up in about 5 minutes, I haven't actually tested an install run just yet. I will do that and see if it works as designed. I assume it should, but you know what they say about assuming. :)
Posted on 07-15-2016 03:31 PM
Looks good! Will definitely be playing with this next week. :)
Thanks as always @mm2270!
Enjoy the weekend,
Matt
Posted on 07-18-2016 04:11 PM
@mm2270 thanks for the great script! Can you give a more advice on what items to change to have all updates checked but greyed out/disabled? We pretty much force all the updates to machines because they have gone through a couple rounds of test deployments before being sent out to all. Management also want the users to be able to defer updates that cause a reboot for a bit (15, 30, 1 hour or install now). Right now I have the reboot time parameter set to 30 min (if needed). So any advice on adding deferment like in your reboot_scheduler.sh (another great script)? Unfortunately my script-fu is not near good enough to meld to two scripts together successfully.
Posted on 07-19-2016 05:43 AM
Hi @Xopher I hadn't thought of merging functionality from the 2 scripts together, but I suppose its something that could be done. Of course, you could just run one script after another in some way to make it all happen, but I know that's a bit more work and could end up with weird results.
I'll look into adding some deferment functionality into the Selectable Software Update script.
As for making the checkboxes greyed out and checked, I'll post that back here a little later. The dialog text needs to be adjusted as well since it doesn't make any sense to say something like 'select the updates to install' if they are all already selected for you.
I have to say though that after looking over the screenshots the other day, I'm not too crazy on this after all, only because the grey text (for the checked but enabled items) is so light its hard to read them clearly, so its not the best user experience in my opinion. Unfortunately, when items are disabled in a dialog, there's no way to control the coloring. That comes from the OS, not something built into cocoaDialog, not that I"d be able to change it there either.
Given this, I may see about adding an extra flag in the script that will simply list the updates that are going to be installed in plain (black) text rather than the checkboxes. It shouldn't be too hard to incorporate.
Posted on 07-19-2016 08:24 AM
Given this, I may see about adding an extra flag in the script that will simply list the updates that are going to be installed in plain (black) text rather than the checkboxes. It shouldn't be too hard to incorporate.
+1 for this - that's basically exactly what I want. :) That and a selectable deferral and i think this script is basically all anyone could ever hope for!
Thanks,
Matt
Posted on 07-19-2016 09:00 AM
Well, for the moment, I'm posting a version below that still uses the checkboxes, but greys them out like shown above, but with some additional modifications to the dialog. I will work on the version that just displays the text in black text like I mentioned above and then post back. Here's the first revision below.
#!/bin/bash
## Script Name: Selectable_SoftwareUpdate.sh (v005)
## Script Author: Mike Morales, @mm2270 on JAMFNation
## Last Update: 2016-07-19
## Path to cocoaDialog (customize to your own location)
cdPath="/Library/Application Support/JAMF/bin/cocoaDialog.app/Contents/MacOS/cocoaDialog"
## Quick sanity check to make sure cocoaDialog is installed in the path specified
if [ ! -e "$cdPath" ]; then
echo "cocoaDialog was not found in the path specified. It may not be installed, or the path is wrong. Exiting..."
exit 1
fi
## Set the installAllAtLogin flag here to 'yes' or leave it blank (equivalent to 'no')
## Function: When the script is run on a Mac that is at the login window, if the flag is set to 'yes',
## it will lock the login window to prevent unintended logins and proceed to install all available updates.
## Once completed, the login window will either be unlocked in the case of no restarts needed,
## or a restart will be done immediately to complete the installations.
installAllAtLogin="yes"
## Set the forceEnableUpdates flag below to 'yes' (or any value) to enable it. This option will change the
## checkbox display to show updates as enabled and non-selectable to the user, meaning they will be forced to
## an "on" state for all. Leaving this value blank will retain the original functionality, allowing the end
## user to select the updates they would like to install.
forceEnableUpdates="~[upload](3744a7a8fa4e45b183e2d26ae2f58167)
"
## Get minor version of OS X
osVers=$( sw_vers -productVersion | cut -d. -f2 )
## Set appropriate Software Update icon depending on OS version
if [[ "$osVers" -lt 8 ]]; then
swuIcon="/System/Library/CoreServices/Software Update.app/Contents/Resources/Software Update.icns"
else
swuIcon="/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns"
fi
## Set appropriate Restart icon depending on OS version
if [[ "$osVers" == "9" ]]; then
restartIcon="/System/Library/CoreServices/loginwindow.app/Contents/Resources/Restart.tiff"
else
restartIcon="/System/Library/CoreServices/loginwindow.app/Contents/Resources/Restart.png"
fi
## Start - Check Casper Suite script parameters and assign any that were passed to the script
## PARAMETER 4: Set the Organization/Department/Division name. Used in dialog titles
## Default string of "Managed" is used if no script parameter is passed
if [[ "$4" != "" ]]; then
orgName="$4"
else
orgName="Managed"
fi
## PARAMETER 5: Set to "no" (case insensitive) to show a single progress bar update for all installations.
## Default value of "yes" will be used if no script parameter is passed
if [[ "$5" != "" ]]; then
shopt -s nocasematch
if [[ "$5" == "no" ]]; then
showProgEachUpdate="no"
else
showProgEachUpdate="yes"
fi
shopt -u nocasematch
else
showProgEachUpdate="yes"
fi
## PARAMETER 6: Set the number of minutes until reboot (only used if installations require it)
## Default value of 5 minutes is assigned if no script parameter is passed
## Special note: Only full integers can be used. No decimals.
## If the script detects a non whole integer, it will fall back on the default 5 minute setting.
if [[ "$6" != "" ]]; then
## Run test to make sure we have a non floating point integer
if [[ $(expr "$6" / "$6") == "1" ]]; then
minToRestart="$6"
else
echo "Non integer, or a decimal value was passed. Setting reboot time to default (5 minutes)"
minToRestart="5"
fi
else
minToRestart="5"
fi
## Parameter 7: Set to the full path of an icon or image file for any dialogs that are not using the
## Apple Software Update icon. This could be a company logo icon for example
## Default icon is set in the following manner:
## If no script parameter is passed, or the icon/image can not be found and JAMF Self Service is present on the Mac, its icon will be used
## If Self Service is not found, the Software Update icon will be used
if [[ "$7" != "" ]]; then
if [[ -e "$7" ]]; then
echo "A custom dialog icon was set: $7"
msgIcon="$7"
else
if [[ -e "/Applications/Self Service.app/Contents/Resources/Self Service.icns" ]]; then
## Self Service present. Use a default Self Service icon if the file specified could not be found
msgIcon="/Applications/Self Service.app/Contents/Resources/Self Service.icns"
else
## Icon file not found, and Self Service not present. Set icon to Software Update
msgIcon="$swuIcon"
fi
fi
else
if [[ -e "/Applications/Self Service.app/Contents/Resources/Self Service.icns" ]]; then
## Self Service present. Use a default Self Service icon if no parameter was passed
msgIcon="/Applications/Self Service.app/Contents/Resources/Self Service.icns"
else
## No parameter passed, and Self Service not present. Set icon to Software Update
msgIcon="$swuIcon"
fi
fi
## End - Check Casper Suite script parameters
## Text displayed in dialog prompting for selections. Customize if desired.
## Two versions:
## One,for when reboot *required* updates are found.
## Two,for when only non-reboot updates are found.
if [[ ! -z "$forceEnableUpdates" ]]; then
swuTextReboots="The following Apple Software Updates will be installed on your Mac when you click "Install".
◀ = Indicates updates that will REQUIRE a reboot of your Mac to complete.
"
else
swuTextReboots="Select the Apple Software Update items you would like to install now from the list below.
◀ = Indicates updates that will REQUIRE a reboot of your Mac to complete.
To install all updates that will not require a reboot, click "Install No Reboot Updates"
"
fi
if [[ ! -z "$forceEnableUpdates" ]]; then
swuTextNoReboots="The following Apple Software Updates will be installed on your Mac when you click "Install".
"
else
swuTextNoReboots="Select the Apple Software Update items you would like to install now from the list below.
"
fi
################################################## ENV VARIABLES #####################################################
## ##
## These variables are gathered to set up the visual environment of the messaging to match the logged in user's ##
## settings. We gather the settings, then change the root account's settings to match. ##
## ##
######################################################################################################################
## Get current logged in user name
loggedInUser=$( ls -l /dev/console | /usr/bin/awk '{ print $3 }' )
echo "Current user is: $loggedInUser"
## Determine logged in user's home directory path
HomeDir=$( dscl . read /Users/$loggedInUser NFSHomeDirectory | awk '{ print $NF }' )
## Get logged in user's Appearance color settings
AquaColor=$( defaults read "$HomeDir/Library/Preferences/.GlobalPreferences" AppleAquaColorVariant 2> /dev/null )
## If user has not changed their settings, value will be null. Set to default 'Aqua' color
if [[ -z "$AquaColor" ]]; then
AquaColor="1"
else
AquaColor="$AquaColor"
fi
## Get logged in user's Keyboard access settings
KeybdMode=$( defaults read "$HomeDir/Library/Preferences/.GlobalPreferences" AppleKeyboardUIMode 2> /dev/null )
## If user has not changed their settings, value will be null. Set to default 'Text boxes and lists only'
if [[ -z "$KeybdMode" ]]; then
KeybdMode="0"
else
KeybdMode="$KeybdMode"
fi
## Set the root account environment settings to match current logged in user's
defaults write /private/var/root/Library/Preferences/.GlobalPreferences AppleAquaColorVariant -int "${AquaColor}"
defaults write /private/var/root/Library/Preferences/.GlobalPreferences AppleKeyboardUIMode -int "${KeybdMode}"
## Restart cfprefsd so new settings will be recognized
killall cfprefsd
################################# Do not modify below this line ########################################
## Function to run when installations are complete
doneRestart ()
{
doneMSG="The installations have completed, but your Mac needs to reboot to finalize the updates.
Your Mac will automatically reboot in $minToRestart minutes. Begin to save any open work and close applications now.
If you want to restart immediately instead, click the "Restart Now" button."
## Display initial message for 30 seconds before starting the progress bar countdown
doneRestartMsg=$( "$cdPath" msgbox --title "$orgName Software Update > Updates Complete"
--text "Updates installed successfully" --informative-text "$doneMSG"
--button1 " OK " --button2 "Restart Now" --icon-file "$msgIcon" --posY top --width 450 --timeout 30 --timeout-format " " )
if [ "$doneRestartMsg" == "1" ]; then
echo "User pressed OK. Moving on to reboot timer..."
elif [ "$doneRestartMsg" == "2" ]; then
echo "User pressed Reboot Now. Rebooting immediately..."
/sbin/shutdown -r now
else
echo "The message timed out. Moving on to reboot timer..."
fi
## Sub-function to (re)display the progressbar window. Developed to work around the fact that
## CD responds to Cmd+Q and will quit. The script continues the countdown. The sub-function
## causes the progress bar to reappear. When the countdown is done we quit all CD windows
showProgress ()
{
## Display progress bar
"$cdPath" progressbar --title "" --text " Preparing to restart this Mac..."
--width 500 --height 90 --icon-file "$restartIcon" --icon-height 48 --icon-width 48 < /tmp/hpipe &
## Send progress through the named pipe
exec 20<> /tmp/hpipe
}
## Close file descriptor 20 if in use, and remove any instance of /tmp/hpipe
exec 20>&-
rm -f /tmp/hpipe
## Create the name pipe input for the progressbar
mkfifo /tmp/hpipe
sleep 0.2
## Run progress bar sub-function
showProgress
echo "100" >&20
timerSeconds=$((minToRestart*60))
startTime=$( date +"%s" )
stopTime=$((startTime+timerSeconds))
secsLeft=$timerSeconds
progLeft="100"
while [[ "$secsLeft" -gt 0 ]]; do
sleep 1
currTime=$( date +"%s" )
progLeft=$((secsLeft*100/timerSeconds))
secsLeft=$((stopTime-currTime))
minRem=$((secsLeft/60))
secRem=$((secsLeft%60))
if [[ $(ps axc | grep "cocoaDialog") == "" ]]; then
showProgress
fi
echo "$progLeft $minRem minutes, $secRem seconds until reboot. Please save any work now." >&20
done
echo "Closing progress bar."
exec 20>&-
rm -f /tmp/hpipe
## Close cocoaDialog. This block is necessary for when multiple runs of the sub-function were called in the script
for process in $(ps axc | awk '/cocoaDialog/{print $1}'); do
/usr/bin/osascript -e 'tell application "cocoaDialog" to quit'
done
## Clean up by deleting the SWUList file in /tmp/
rm /tmp/SWULIST
## Delay 1/2 second, then force reboot
sleep 0.5
shutdown -r now
}
## Function to install selected updates, updating progress bar with information
installUpdates ()
{
if [[ "${restartReq}" == "yes" ]]; then
installMSG="Installations are now running. Please do not shut down your Mac or put it to sleep until the installs finish.
IMPORTANT:
Because you chose some updates that require a restart, we recommend saving any important documents now. Your Mac will reboot soon after the installations are complete."
elif [[ "${restartReq}" == "no" ]] || [[ "${restartReq}" == "" ]]; then
installMSG="Updates are now installing. Please do not shut down your Mac or put it to sleep until the installs finish."
fi
## Sub-function to display both a button-less CD window and a progress bar
## This sub routine gets called by the enclosing function. It can also be called by
## the install process if it does not see 2 instances of CD running
showInstallProgress ()
{
## Display button-less window above progress bar, push to background
"$cdPath" msgbox --title "$orgName Software Update > Installation" --text "Installations in progress"
--informative-text "${installMSG}" --icon-file "${msgIcon}" --width 450 --height 184 --posY top &
## Display progress bar
echo "Displaying progress bar window."
"$cdPath" progressbar --title "" --text " Preparing to install selected updates..."
--posX "center" --posY 198 --width 450 --float --icon installer < /tmp/hpipe &
## Send progress through the named pipe
exec 10<> /tmp/hpipe
}
## Close file descriptor 10 if in use, and remove any instance of /tmp/hpipe
exec 10>&-
rm -f /tmp/hpipe
## Create the name pipe input for the progressbar
mkfifo /tmp/hpipe
sleep 0.2
## Run the install progress sub-function (shows button-less CD window and progressbar
showInstallProgress
if [[ "$showProgEachUpdate" == "yes" ]]; then
echo "Showing individual update progress."
## Run softwareupdate in verbose mode for each selected update, parsing output to feed the progressbar
## Set initial index loop value to 0; set initial update count value to 1; set variable for total updates count
i=0;
pkgCnt=1
pkgTotal="${#selectedItems[@]}"
for index in "${selectedItems[@]}"; do
UpdateName="${progSelectedItems[$i]}"
echo "Now installing ${UpdateName}..."
/usr/sbin/softwareupdate --verbose -i "${index}" 2>&1 | while read line; do
## Re-run the sub-function to display the cocoaDialog window and progress
## if we are not seeing 2 items for CD in the process list
if [[ $(ps axc | grep "cocoaDialog" | wc -l | sed 's/^ *//') != "2" ]]; then
killall cocoaDialog
showInstallProgress
fi
pct=$( echo "$line" | awk '/Progress:/{print $NF}' | cut -d% -f1 )
echo "$pct Installing ${pkgCnt} of ${pkgTotal}: ${UpdateName}..." >&10
done
let i+=1
let pkgCnt+=1
done
else
## Show a generic progress bar that progresses through all installs at once from 0-100 %
echo "Parameter 5 was set to "no". Showing single progress bar for all updates"
softwareupdate --verbose -i "${SWUItems[@]}" 2>&1 | while read line; do
## if we are not seeing 2 items for CD in the process list
if [[ $(ps axc | grep "cocoaDialog" | wc -l | sed 's/^ *//') != "2" ]]; then
killall cocoaDialog
showInstallProgress
fi
pct=$( echo "$line" | awk '/Progress:/{print $NF}' | cut -d% -f1 )
echo "$pct Installing ${#SWUItems[@]} updates..." >&10
done
fi
echo "Closing progress bar."
exec 10>&-
rm -f /tmp/hpipe
## Close all instances of cocoaDialog
echo "Closing all cocoaDialog windows."
for process in $(ps axc | awk '/cocoaDialog/{print $1}'); do
/usr/bin/osascript -e 'tell application "cocoaDialog" to quit'
done
## If any installed updates required a reboot...
if [[ "${restartReq}" == "yes" ]]; then
## ...then move to the restart phase
doneRestart
## If no installed updates required a reboot, display updates complete message instead
elif [[ "${restartReq}" == "no" ]]; then
echo "Showing updates complete message."
doneMSG="The installations have completed successfully. You can resume working on your Mac."
"$cdPath" msgbox --title "$orgName Software Update > Updates Complete"
--text "Updates installed successfully" --informative-text "$doneMSG"
--button1 " OK " --posY top --width 450 --icon-file "$msgIcon"
## Clean up by deleting the SWUList file in /tmp/ before exiting the script
echo "Cleaning up SWU list file."
rm /tmp/SWULIST
exit 0
fi
}
## Function to assess which items were checked, and create new arrays
## used for installations and other functions
assessChecks ()
{
## Check to see if the installNoReboots flag was set by the user
if [[ "$installNoReboots" == "yes" ]]; then
echo "User chose to install all non reboot updates. Creating update(s) array and moving to install phase"
## If flag was set, build update arrays from the noReboots array
for index in "${noReboots[@]}"; do
selectedItems+=( "${SWUItems[$index]}" )
hrSelectedItems+=( "${SWUList[$index]}" )
progSelectedItems+=( "${SWUProg[$index]}" )
done
## Automatically set the restart required flag to "no"
restartReq="no"
## Then move on to install updates function
installUpdates
fi
## If installNoReboots flag was not set, generate array of formatted
## checkbox indexes for parsing based on the selections from the user
i=0;
for state in ${Checks[*]}; do
checkboxstates=$( echo "${i}-${state}" )
let i+=1
## Set up an array we can read through later with the state of each checkbox
checkboxfinal+=( "${checkboxstates[@]}" )
done
for check in "${checkboxfinal[@]}"; do
if [[ "$check" =~ "-1" ]]; then
## First, get the index of the checked item
index=$( echo "$check" | cut -d- -f1 )
## Second, generate 3 new arrays:
## 1) Short names of the updates for the installation
## 2) Names of updates as presented in the dialog (for checking restart status)
## 3) Names of the updates for updating the progress bar
selectedItems+=( "${SWUItems[$index]}" )
hrSelectedItems+=( "${SWUList[$index]}" )
progSelectedItems+=( "${SWUProg[$index]}" )
fi
done
echo "The following updates will be installed: ${progSelectedItems[@]}"
## Determine if any of the checked items require a reboot
restartReq="no"
for item in "${hrSelectedItems[@]}"; do
if [[ $(echo "${item}" | grep "^◀") != "" ]]; then
echo "At least one selected update will require reboot. Setting the restartReq flag to "yes""
restartReq="yes"
break
fi
done
echo "Restart required?: ${restartReq}"
## If we have some selected items, move to install phase
if [[ ! -z "${selectedItems[@]}" ]]; then
echo "Updates were selected"
installUpdates
fi
}
## The initial message function
startDialog ()
{
## Generate array of SWUs for dialog
z=0
while read SWU; do
SWUList+=( "$SWU" )
if [[ ! -z "$forceEnableUpdates" ]]; then
checksOnArr+=("$z")
let z=$((z+1))
fi
done < <(echo "${readSWUs}")
## Generate array of SWUs for progress bar
while read item; do
SWUProg+=( "${item}" )
done < <(echo "${progSWUs}")
## Generate array of SWUs for installation
while read swuitem; do
SWUItems+=( "$swuitem" )
done < <(echo "${installSWUs}")
## Generate an array of indexes for any non-reboot updates
for index in "${!SWUList[@]}"; do
if [[ $(echo "${SWUList[$index]}" | grep "^◀") == "" ]]; then
noReboots+=( "$index" )
fi
done
if [[ ! -z "$forceEnableUpdates" ]]; then
button3Label=""
else
button3Label="Install No Reboot Updates"
fi
## Show dialog with selectable options
if [[ ! -z "${noReboots[@]}" ]]; then
echo "There are some non reboot updates available. Showing selection screen to user"
SWUDiag=$( "$cdPath" checkbox --title "$orgName Software Update" --items "${SWUList[@]}" --checked "${checksOnArr[@]}" --disabled "${checksOnArr[@]}"
--label "$swuTextReboots" --button1 " Install " --button2 " Cancel " --cancel "button2" --button3 "$button3Label"
--icon-file "$msgIcon" --icon-height 80 --icon-width 80 --width 500 --posY top )
## Get the button pressed and the options checked
Button=$( echo "$SWUDiag" | awk 'NR==1{print $0}' )
Checks=($( echo "$SWUDiag" | awk 'NR==2{print $0}' ))
## Set up a non array string from the checkboxes returned
ChecksNonArray=$( echo "$SWUDiag" | awk 'NR==2{print $0}' )
## If the "Install" button was clicked
if [[ "$Button" == "1" ]]; then
echo "User clicked the "Install" button."
## Check to see if at least one box was checked
if [[ $( echo "${ChecksNonArray}" | grep "1" ) == "" ]]; then
echo "No selections made. Alerting user and returning to selection screen."
"$cdPath" msgbox --title "$orgName Software Update" --text "No selections were made"
--informative-text "$(echo -e "You didn't select any updates to install.
If you want to cancel out of this application, click the "Cancel" button in the window instead, or press the Esc key.
The Software Update window will appear again momentarily.")"
--button1 " OK " --timeout 10 --timeout-format " " --width 500 --posY top --icon caution
## Because we are restarting the function, first empty all previously built arrays
## Credit to Cem Baykara (@Cem - JAMFNation) for discovering this issue during testing
SWUList=()
SWUProg=()
SWUItems=()
## Now restart this function after the alert message times out
startDialog
else
## "Install" button was clicked and items checked. Run the assess checkbox function
echo "Selections were made. Moving to assessment function..."
assessChecks
fi
elif [[ "$Button" == "3" ]]; then
## "Install No Reboot Updates" button was clicked. Set the installNoReboots flag to "yes" and skip to check assessment
echo "User clicked the "Install No Reboot Updates" button."
installNoReboots="yes"
assessChecks
else
echo "User chose to Cancel. Exiting..."
exit 0
fi
else
## No non-reboot updates were available. Display a different dialog to the user
echo "No non-reboot updates found, but other updates available. Showing selection dialog to user"
SWUDiag=$( "$cdPath" checkbox --title "$orgName Software Update" --items "${SWUList[@]}" --checked "${checksOnArr[@]}" --disabled "${checksOnArr[@]}"
--label "$swuTextNoReboots" --button1 " Install " --button2 " Cancel " --cancel "button2"
--icon-file "$swuIcon" --icon-height 80 --icon-width 80 --width 500 --posY top --value-required
--empty-text "$(echo -e "You must check at least one item before clicking "Install".
If you want to exit, click "Cancel" or press the esc key.")" )
## Get the button pressed and the options checked
Button=$( echo "$SWUDiag" | awk 'NR==1{print $0}' )
Checks=($( echo "$SWUDiag" | awk 'NR==2{print $0}' ))
if [[ "$Button" == "1" ]]; then
## "Install" button was clicked. Run the assess checkbox function
echo "User clicked the "Install" button"
assessChecks
else
echo "User chose to Cancel from the selection dialog."
echo "Cleaning up SWU list file. Exiting..."
rm /tmp/SWULIST
exit 0
fi
fi
}
## Function to lock the login window and install all available updates
startLockScreenAgent ()
{
## Note on this function: To make the script usable outside of a Casper Suite environment,
## we are using the Apple Remote Management LockScreen.app, located inside the AppleVNCServer bundle.
## This bundle and corresponding app is installed by default in all recent versions of OS X
## Set a flag to yes if any updates in the list will require a reboot
while read line; do
if [[ $(echo "$line" | grep "^◀") != "" ]]; then
rebootsPresent="yes"
break
fi
done < <(echo "$readSWUs")
## Define the name and path to the LaunchAgent plist
PLIST="/Library/LaunchAgents/com.LockLoginScreen.plist"
## Define the text for the xml plist file
LAgentCore="<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.LockLoginScreen</string>
<key>RunAtLoad</key>
<true/>
<key>LimitLoadToSessionType</key>
<string>LoginWindow</string>
<key>ProgramArguments</key>
<array>
<string>/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/MacOS/LockScreen</string>
<string>-session</string>
<string>256</string>
<string>-msg</string>
<string>Updates are currently being installed on this Mac. It will automatically be restarted or returned to the login window when installations are complete.</string>
</array>
</dict>
</plist>"
## Create the LaunchAgent file
echo "Creating the LockLoginScreen LaunchAgent..."
echo "$LAgentCore" > "$PLIST"
## Set the owner, group and permissions on the LaunchAgent plist
echo "Setting proper ownership and permissions on the LaunchAgent..."
chown root:wheel "$PLIST"
chmod 644 "$PLIST"
## Use SIPS to copy and convert the SWU icon to use as the LockScreen icon
## First, back up the original Lock.jpg image
echo "Backing up Lock.jpg image..."
mv /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg
/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg.bak
## Now, copy and convert the SWU icns file into a new Lock.jpg file
## Note: We are converting it to a png to preserve transparency, but saving it with the .jpg extension so LockScreen.app will recognize it.
## Also resize the image to 400 x 400 pixels so its not so honkin' huge!
echo "Creating SoftwareUpdate icon as png and converting to Lock.jpg..."
sips -s format png "$swuIcon" --out /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg
--resampleWidth 400 --resampleHeight 400
## Now, kill/restart the loginwindow process to load the LaunchAgent
echo "Ready to lock screen. Restarting loginwindow process..."
kill -9 $(ps axc | awk '/loginwindow/{print $1}')
## Install all available Software Updates
echo "Screen locked. Installing all available Software Updates..."
/usr/sbin/softwareupdate --install --all
if [ "$?" == "0" ]; then
## Delete LaunchAgent and reload the Login Window
echo "Deleting the LaunchAgent..."
rm "$PLIST"
sleep 1
if [[ "$rebootsPresent" == "yes" ]]; then
## Put the original Lock.jpg image back where it was, overwriting the SWU Icon image
echo "The rebootsPresent flag was set to 'yes' Replacing Lock.jpg image and immediately rebooting the Mac..."
mv /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg.bak
/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg
## Kill the LockScreen app and restart immediately
killall LockScreen
/sbin/shutdown -r now
else
## Put the original Lock.jpg image back where it was, overwriting the SWU Icon image
echo "The rebootsPresent flag was not set. Replacing Lock.jpg image and restoring the loginwindow..."
mv /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg.bak
/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg
## Kill/restart the login window process to return to the login window
kill -9 $(ps axc | awk '/loginwindow/{print $1}')
fi
else
echo "There was an error with the installations. Removing the Agent and unlocking the login window..."
rm "$PLIST"
sleep 1
mv /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg.bak
/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/Resources/Lock.jpg
## Kill/restart the login window process to return to the login window
kill -9 $(ps axc | awk '/loginwindow/{print $1}')
exit 0
fi
}
## The script starts here
## Gather available Software Updates and export to a file
echo "Pulling available Software Updates..."
/usr/sbin/softwareupdate -l > /tmp/SWULIST
echo "Finished pulling available Software Updates into local file"
echo "Checking to see what updates are available..."
## Generate list of readable items and installable items from file
readSWUs=$( cat /tmp/SWULIST | awk -F"," '/recommended/{print $2,$1}' | sed -e 's/[0-9]*K [recommended][ *]//g;s/[restart] */◀ /g' | sed 's/[ ]//g' )
progSWUs=$( cat /tmp/SWULIST | awk -F"," '/recommended/{print $2,$1}' | sed -e 's/[0-9]*K [recommended][ *]//g;s/[restart] *//g' | sed 's/[ ]//g' )
installSWUs=$( cat /tmp/SWULIST | grep -v 'recommended' | awk -F'\* ' '/*/{print $NF}' )
## First, make sure there's at least one update from Software Update
if [[ -z "$readSWUs" ]]; then
echo "No pending Software Updates found for this Mac. Exiting..."
exit 0
elif [[ ! -z "$readSWUs" ]] && [[ "$loggedInUser" != "root" ]]; then
echo "Software Updates are available, and a user is logged in. Moving to initial dialog..."
startDialog
elif [[ ! -z "$readSWUs" ]] && [[ "$loggedInUser" == "root" ]]; then
if [ "$installAllAtLogin" == "yes" ]; then
echo "SWUs are available, no-one logged in and the installAllAtLogin flag was set. Locking screen and installing all updates..."
startLockScreenAgent
else
echo "SWUs are available, no-one logged in but the installAllAtLogin flag was not set. Exiting..."
exit 0
fi
fi
Essentially, the force option can be enabled with a flag by entering any value on line 29 in the script where it shows
forceEnableUpdates=""
When that's on, it checks and grays out the checkboxes and changes the dialog text and buttons accordingly.
A couple of quick screenshots to show the difference. Without the forceEnableUpdates
option enabled, we see the original behavior:
With that option enabled with any text in the variable, it changes the behavior to:
As shown, the dialog text changes, and the "Install No Reboot Updates" button disappears in the 2 instance.
As soon as I have something to show for simply making the text show up in a list with no checkboxes, I'll post back with that.
Posted on 07-19-2016 10:17 AM
thank you, sir! appreciate all your work on this.
Posted on 07-19-2016 01:16 PM
Yes, thank you for all your contributions (and everyone's) to this community!
Posted on 07-19-2016 04:34 PM
@mm2270 In the modified script above the section between lines 524 to 634 are not valid as there is a syntax error around line 524.
Thanks for all the help, you were correct we were trying the script using sh, changed that behaviour and the unmodified script works well.
Also would like to have a deferral section added to this to make it complete.
Posted on 07-19-2016 05:00 PM
@mm2270 fantastic work! How do you feel about putting the script on github so others can contribute, if desired?
Posted on 07-19-2016 05:58 PM
@mm2270 Found that if you change "didn't" to "did not" the syntax is corrected
Posted on 07-19-2016 06:13 PM
From the BASH scripting guide:
escape [backslash]. A quoting mechanism for single characters.
X escapes the character X. This has the effect of "quoting" X, equivalent to 'X'. The may be used to quote " and ', so they are expressed literally.
So if "didn't" is changed to "didn't" the syntax is also correct.