If you had a way of determining the progress in the script, you could update a CocoaDialog progress bar every second or so to reflect it. I suppose that could be done by monitoring the size of the file in the waiting room and comparing it to the total file size, which you'd probably need to manually determine and pass to the script somehow.
Are you using a standard caching policy in your JSS policy, like using the "Cache" option for the package? Or, are you using something like an installer that downloads and then installs the "cached" package into a special directory for use later?
If you're using the built in policy option, I don't think there's a way to do it, since the standard policy stuff doesn't have any progress output I'm aware of you can send back to cocoaDialog to "move" the progress bar.
One possible way, which I use a bit in one of my scripts, but haven't used against JSS hosted pkgs, is to do a curl command on the package on your http Casper Share and get the size of the file, which shows up under a line called "Length" You can then use that info in a loop process that runs a du command on the file being downloaded and does math to convert to a percentage. In other words, use the total size and compare against the current size of the file being downloaded to figure out how much percentage-wise its complete. That can then be piped back to cocoaDialog to use for the percentage of the progressbar.
Does that make any sense?
Edit: @alexjdale basically said the same thing I just did.
@mm2270 it does make sense and yes @alexjdale essentially said the same thing, ha. Thank you both for responding.
We are just using the cache option/policy from the JSS and then a separate policy to install it. Instead of using curl, you think I could hard code the size of the package since it's static?
Also, would the script with the cocoadialog commands, order wise, have to kick off after the cache policy started? So basically, in the policy, it would go "cache xyz.pkg" then run "progressbar.sh"? I'm just wondering if the script will kick off even if the package hasn't finished caching yet.
Yes, you could hardcode a value of the actual full file size into the script to use (or pass it as a parameter) instead of getting it dynamically with curl.
As for the order, what would need to happen is the caching policy would need to be kicked off, maybe with something like a jamf policy -trigger <sometrigger>
call, but placed into the background so the script can continue and then begin checking the local file size. This may all be kind of tricky, because it can take several moments for the download to even start, so essentially, you'd need to include some kind of wait process, where the script waits to see the initial file created in the /Library/Application Support/JAMF/Waiting Room/ directory before it begins the progress bar perhaps, or, pop up the progress bar, but with the understanding that "progress" on the download may not start for a short period.
If I have the time I can try putting together a sample script you can use. I think this is possible, but since I can't say I've done exactly this, I'd have to play with it to see how it would work in practice.
Hope that helps a little for the moment.
@mm2270 Yea I think the tricky part is getting the cache to somehow delay so the script can run. We are using a custom trigger for the caching policy to start.
Although now that I write this, I realize I could place it in the script that calls the cache policy to start. I'm just not sure how to put the custom trigger policy on a delay so the script can continue to run to get the percentage once the file starts to cache using cocoadialog.
Thanks for any help you can provide and I may reach out to my rep to see if there is a way to delay a custom trigger policy and then make it resume again, all in the same script. If/when I come up with anything in terms of workflow, I'll post it back here.
In scripts executed by Casper, I've found that you have to use a launchdaemon to truly spin off another execution thread.
You could look at my code here to get an idea of how to echo out and run a launchdaemon/script: https://jamfnation.jamfsoftware.com/discussion.html?id=14940
You should be able to create and load a launchdaemon that runs the policy trigger to cache the package. The main script could then monitor the download and provide progress bar updates. I also have a progress bar update loop in there that you might find useful with some modification.
CocoaDialog can be finicky and you have to use the beta of version 3 for progress to work but here is code to show progress of caching a package. I have a function for when I call the installer too if you need that. You have to tell it the size of the package since it can't be determined dynamically with the way packages ae cached currently.
cdPath="/Path/To/cocoaDialog.app/Contents/MacOS/cocoaDialog"
cdIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ApplicationsFolderIcon.icns"
cdTitle="Dialog Title"
pkgTotal=12064712 #Must know the size of the package
pkgPartial=0
downloadFolder="/Library/Application Support/JAMF/Downloads"
waitingRoomFolder="/Library/Application Support/JAMF/Waiting Room"
dmgName="Package.dmg"
## CocoaDialog Function
dialogDownload()
{
## Create the name pipe input for the progressbar & set brief pause
rm -f /tmp/hpipe
mkfifo /tmp/hpipe
sleep 0.2
## Display progress bar with initial text settings
"$cdPath" progressbar --stoppable --title "Upgrade Package Download Progress" --text "Downloading..." --float < /tmp/hpipe > /tmp/_out &
exec 3< <(yes)
## Send progress through the named pipe
exec 3<> /tmp/hpipe
if { >&3; } 2> /dev/null
then
writeLog "Pipe RW verified"
else
writeLog "Pipe RW failed"
fi
#Determine PID of jamf process so it can be killed.
trap : SIGTERM SIGINT
echo $$
(jamf policy -event PayloadName ) > /tmp/_out2 &
jamfPID=$!
## Give the policy time to start
sleep 5
#Initialize error counter
counter=0
#While loop to generate package download percent
while [[ $pkgPartial != $pkgTotal ]]
do
#Watches for the output of CocoaDialog to equal stopped if "Stop" button is clicked. Cancels install.
dialogStopCheck=`cat /tmp/_out`
if [[ "$dialogStopCheck" = "stopped" ]]
then
kill $jamfPID
rm -f /tmp/_out
rm -f /tmp/_out2
writeLog "Upgrade cancelled by user."
exit
fi
#Watches for possible jamf binary being upgraded which will fail download of package.
binaryUpgradeCheck=`cat /private/tmp/_out2 | grep "Upgrading jamf binary..."`
if [[ "$binaryUpgradeCheck" != "" ]]
then
kill $jamfPID
rm -f /tmp/_out
rm -f /tmp/_out2
writeLog "jamf binary upgrade detected. Fixing..."
jamf policy -event PayloadName >> "$logFile"
sleep 20
dialogDownload
fi
#Error counter check. Die at 10 errors.
if [[ $counter -gt 9 ]]
then
echo "Package Download failed..." >&3
sleep 5
kill $jamfPID
writeLog "Package download failed."
exit
else
#If the package exists in the download folder determine percentage downloaded.
if [[ -f "$downloadFolder"/"$dmgName" ]]
then
pkgPartial=`du "$downloadFolder"/"$dmgName" | awk '{print $1}'`
modifiedTime=`stat -s "$downloadFolder"/"$dmgName" | awk '{print $10}' | sed 's/.*=//'`
currentTime=`date +%s`
timeDifference=`expr $currentTime - $modifiedTime`
#If the downloading file has not been updated in the 10 seconds relative to the current time download may have stalled. Increment error counter.
if [[ $timeDifference -gt 10 ]]
then
echo "$xferPercentage Download may have stalled... ${xferPercentage}%" >&3
sleep 5
counter=$(expr $counter + 1)
else
#Calculate percentage
if [[ $pkgPartial =~ ^-?[0-9]+$ ]]
then
xferPercentage=`echo "scale=0; $pkgPartial*100/$pkgTotal" | bc`
echo "$xferPercentage Downloading... ${xferPercentage}%" >&3
fi
fi
#Determine if the file has been moved to the JAMF Waiting room. Exit loop if so to begin install.
elif [[ -f "$waitingRoomFolder"/"$dmgName" ]]
then
writeLog "$dmgName downloaded to $waitingRoomFolder"
break
else
counter=$(expr $counter + 1)
fi
fi
done
## Turn off progress bar by closing file descriptor 3 and removing the named pipe
echo "Closing progress bar."
exec 3>&-
rm -f /tmp/hpipe
}
@iJake Thanks so much for your response.
It seems to just pass the script for me and not run any of the cocoadialog stuff. Besides the paths at the top of the script, where else needs to be customized?
Currently, I've just filled in my jamfPID, like so:
(jamf policy -event PayloadName ) > /tmp/_out2 &
jamfPID=863
Am I missing something else? Would a custom trigger go in place of "PayloadName"? Let me know your thoughts. Thanks again.
Yeah, sorry, I stripped out my policy names. One of the payloads is the cache policy and another is me installing our agent health daemon. You can just strip that whole part since you don't have it. It's where it's checking if an agent upgrade is happening. You'll also need to fill in the DMG file name and its size. You could always get the name dynamically but I didn't have a need at the time.
@iJake Thanks and yea I filled in everything at the top, size, name, etc. I was just having trouble seeing where it was kicking off the caching policy.
From what I can see, if it starts to cache, everything else gets ignored until it's done. How are you getting it to start caching and then also kicking off the cocoadialog stuff? Separate launchdaemon?
This line
(jamf policy -event PayloadName ) > /tmp/_out2 &
It calls the policy, redirects it to that file and then runs its as a background process so the scrip can continue on.
This line
"$cdPath" progressbar --stoppable --title "Upgrade Package Download Progress" --text "Downloading..." --float < /tmp/hpipe > /tmp/_out &
Calls CoocoaDialog with it reading in from the named pipe.
Then anywhere you see >&3 we are sending data to that named pipe.
Let me know if that makes sense. If not, i'd be happy to jump on a WebEx and see if we can get it working for you.
@iJake totally makes sense ha. However, I put my custom trigger for the cache policy in place of PayloadName:
(jamf policy -event mytrigger ) > /tmp/_out2 &
and the script basically runs for 2 seconds and finishes. It seems it doesn't call the policy and therefore no progress bars and finishes with nothing happening. If I do "jamf policy -event mytrigger" in terminal on the same mac, the policy works as expected and starts to cache my package. Any ideas?
First, let's get you the logging function I use so we can see what's going
softwareTitle=cachedDialogTest
logFolder="/Library/Logs/FOLDERNAME"
logFile="$logFolder"/"$softwareTitle.log"
timeStamp=$(date "+%Y %b %d %T")
consoleUser=$(stat -f %Su "/dev/console")
logSize=$(stat -f%z $LogFile)
## LOGGING
writeLog(){ echo "[$timeStamp] [$softwareTitle] [$consoleUser]: $1" >> $logFile; }
[[ -d $logFolder ]] || mkdir -p -m 775 "$logFolder"
[[ $logSize -ge $maxSize ]] && rm -rf "$logFile"
Second, are you making sure to tell it the name of the package you are caching?
dmgName="Package.dmg"
@iJake Thanks for your continued responses.
So it makes the log folder but no log inside. The JSS policy log says the policy is successful and just says script exit code 0. So, no logging going on or being made, besides the log folder being created. Again, the script finishes running in about 5 seconds. Running the script from terminal yields the same result.
And yes, package name is correct.
Run the script locally in bash debug mode.
sudo bash -x scriptName.sh
@iJake Here is the output:
+ softwareTitle=cachedDialogTest
+ logFolder=/Library/Logs/jamfcache
+ logFile=/Library/Logs/jamfcache/cachedDialogTest.log
++ date '+%Y %b %d %T'
+ timeStamp='2016 Jan 22 17:00:51'
++ stat -f %Su /dev/console
+ consoleUser=cp
++ stat -f%z
+ logSize=0
+ [[ -d /Library/Logs/jamfcache ]]
+ [[ 0 -ge '' ]]
+ rm -rf /Library/Logs/jamfcache/cachedDialogTest.log
+ cdPath=/Applications/Utilities/cocoaDialog.app/Contents/MacOS/cocoaDialog
+ cdIcon=/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ApplicationsFolderIcon.icns
+ cdTitle='OS X Installer Cache Progress'
+ pkgTotal=6194494570
+ pkgPartial=0
+ downloadFolder='/Library/Application Support/JAMF/Downloads'
+ waitingRoomFolder='/Library/Application Support/JAMF/Waiting Room'
+ dmgName=InstallOSX_10.11.3_15D21.pkg.zip
And that's where it ends and goes back to the prompt.
OH. You're never calling the function :)
Put dialogDownload at the part where you want the download and dialog to happen.
@iJake Ok I think I'm just tired ha but how would that work exactly? Can you give me an example?
Sure. So, what i've sent are some variable definition and functions. To make the function work you need to call it. It takes no arguments so all you do is type out dialogDownload much like you would call a variable just without the $ in front of it. You can see that the logging I use is really just a function and its being called by having its name typed out.
Variable Definitions... #definitions go at the top
variable="stuff"
Function Definitions... #then we create the functions
dialogDownlod()
{
lots of code stuffs here
}
dialogDownload #now we call the function
At any point before or after the function you can have other code
@iJake Yup that did it. I feel rather foolish. I was just missing the function at the end the whole time.
The only thing that remains is that the progress stays at 0% and never updates. If I stop the cache through the prompt, and then check the file, it's actually 1 or 2 GB already, but before that it just stays stuck at 212k or whatever the first value is. For the value "partialPkg=0" that stays at 0 correct?
Yeah, it should stay at that 0. Its just the initial definition and we dynamically update it lower. If you run the working script now in debug you should see it doing all the math if its working. Might have to cancel it so you can stop and read the output.
This line does the work to update that number
pkgPartial=`du "$downloadFolder"/"$dmgName" | awk '{print $1}'`
@iJake Ok cool.
Well when I go into debug mode in terminal, it just goes crazy and writes forever it seems, all while the progress doesn't move and the display shows 0%. Again, if i cancel it, and click on the file in /Library/Application Support/JAMF/Downloads, it definitely is downloading and usually a GB or two by the time I hit cancel.
Any ideas on what to check?
@perrycj Copy and paste some of the debug output where it is doing that line i showed above.
@iJake sure thing.
++ stat -s '/Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip'
++ awk '{print $10}'
++ sed 's/.*=//'
+ modifiedTime=1453503069
++ date +%s
+ currentTime=1453503070
++ expr 1453503070 - 1453503069
+ timeDifference=1
+ [[ 1 -gt 10 ]]
+ [[ 747424 =~ ^-?[0-9]+$ ]]
++ echo 'scale=0; 747424*100/6194494570'
++ bc
+ xferPercentage=0
+ echo '0 Downloading... 0%'
+ [[ 747424 != 6194494570 ]]
++ cat /tmp/_out
+ dialogStopCheck=
+ [[ '' = s opped ]]
++ cat /private/tmp/_out2
++ grep 'Upgrading jamf binary...'
+ binaryUpgradeCheck=
+ [[ '' != '' ]]
+ [[ 0 -gt 9 ]]
+ [[ -f /Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip ]]
++ du '/Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip'
++ awk '{print $1}'
+ pkgPartial=747584
++ stat -s '/Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip'
++ awk '{print $10}'
++ sed 's/.*=//'
+ modifiedTime=1453503070
++ date +%s
+ currentTime=1453503070
++ expr 1453503070 - 1453503070
+ timeDifference=0
+ [[ 0 -gt 10 ]]
+ [[ 747584 =~ ^-?[0-9]+$ ]]
++ echo 'scale=0; 747584*100/6194494570'
++ bc
+ xferPercentage=0
+ echo '0 Downloading... 0%'
+ [[ 747584 != 6194494570 ]]
++ cat /tmp/_out
+ dialogStopCheck=
+ [[ '' = s opped ]]
++ cat /private/tmp/_out2
++ grep 'Upgrading jamf binary...'
+ binaryUpgradeCheck=
+ [[ '' != '' ]]
+ [[ 0 -gt 9 ]]
+ [[ -f /Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip ]]
++ du '/Library/Application Support/JAMF/Downloads/InstallOSX_10.11.3_15D21.pkg.zip'
++ awk '{print $1}'
+ pkgPartial=747808
It looks like it's getting the partial part but just not updating the %
I see it doing the math for the percentage, too. Hmm. Try changing
xferPercentage=`echo "scale=0; $pkgPartial*100/$pkgTotal" | bc`
xferPercentage=$(echo "scale=0; $pkgPartial*100/$pkgTotal" | bc)