Hello! Ah, yes - this is a change I'd like to make myself actually, and remove the dependency on jamfhelper.
You can do something like this in python:
from subprocess import Popen, PIPE
script = """tell application "Finder"
activate
display dialog "Hello!"
end tell
"""
proc = Popen(['sudo', '-u', current_user(), 'osascript', '-'],
stdin=PIPE,
stdout=PIPE)
out = proc.communicate(script)[0]
(edited to include subprocess imports)
Btw, I am significantly reworking the script at the moment:
https://github.com/UoE-macOS/jss/pull/12
if you're interested in contributing I'd be very happy to take a pull request!
@mbezzo If you're tossing up a list to select from, might want tot stick with System Events
(as opposed to Finder
) so you retain focus.
Thanks, @donmontalvo I use System Events in all of my scripts just for that reason! :)
And @glee_edin, this is LITERALLY my first time working with Python - you definitely don't want me working on that script yet! That being said, I really like the script and want to get it working with osascript, so I'm going to keep chipping away at it. If you do continue to work on it, feel free to comment the crap out of it and/or get osascript working for a pop up and I'll gladly test it out/provide feedback!
Thanks all,
Matt
@mbezzo - you might like this:
https://github.com/UoE-macOS/jss/blob/master/coreconfig-softwareupdate-run.py
I've converted the user interaction to use osascript, except when we install unattended updates, where I still need to use JamfHelper to lock out the screen (actually, now I think about it, I think there's a utility as part of ARD that can do the same thing...)
Please feel free to open issues if you think of any improvements that you're not comfortable implementing yourself. The script is a bit of a mess - one day soon I'll clean it up to pass pylint and do some refactoring
Oooo, awesome!!! THANK YOU! Hoping to spend some time on this today. :)
@glee_edin,
Alright, Trying your new script as is (so I can see how it works first) but I'm not getting a pop-up, here's the terminal output (I'm just hard coding the variables nomrally passed by the JSS):
SEA-VM-BEZZTST1:~ bezzo$ sudo python /Users/bezzo/Desktop/NewSoftwareUpdates 2.py
Password:
Checking for updates
Processing macOS High Sierra 10.13.2 Supplemental Update
Downloading macOS High Sierra 10.13.2 Supplemental Update-
091-58876 is already downloaded
Setting up the updates index file
Setting up 091-58876 to install at logout
Setting updates to run on logout
Created deferral file - Ok to defer until 2018-01-30 08:41:32.933540
User elected to defer update
SEA-VM-BEZZTST1:~ bezzo$
Any ideas?
Thanks!
Matt
Ah, looks like things are different in 10.13:
osascript[940] <Error>: AppleEvents: received mach msg which wasn't complex type as expected in getMemoryReference.
The sudo trick might not work any longer (though if it works in bash, it ought to be ok in python). I've brought up a 10.13 test box now which has uncovered another couple of bugs. I'll let you know when I've got it working properly on 10.13.
Sounds good - REALLY appreciate your work on this!
FWIW, I recall some time ago that osascript's max timeout was 8947878 seconds - not sure if that's changed but in general that's what I'd recommend for "unlimited" timeouts in osascript.
Since around 10.11 I've been using the launchctl asuser
command to run commands as the user. This can apply to Applescript calls, but also to regular bash calls, such as running a security binary command as the logged in user to affect their login keychain. It generally works well.
I don't know how that would be incorporated into a python script though, since the syntax is going to be different from bash.
FWIW, our template for cases were we need to create a Launch Agent and launch an item as the current user...
#!/bin/bash
application="/Applications/MyApplication.app"
launchagent="/Library/LaunchAgents/com.company.myapplication.plist"
# Install Launch Agent
echo '<?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>Disabled</key>
<false/>
<key>Label</key>
<string>com.company.myapplication</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/MyApplication.app/Contents/MacOS/MyApplication</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
</dict>
</plist>' > "$launchagent"
/bin/chmod 644 "$launchagent"
# Launch it for the current user
current_user=$( python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");' )
if [[ $current_user ]]; then
current_uid=$(id -u $current_user)
/bin/launchctl bootstrap gui/"$current_uid" "$launchagent"
# Older versions of OSX <= 10.9 don't respond to the above command, use the deprecated open command to launch on them
if [[ $? -ne 0 ]]; then
/bin/launchctl asuser "$current_uid" open "$application"
fi
fi
exit 0
Thanks all - just to update the thread:
- Using sudo turns out to work fine (though I think the launchctl option is probably much better these days)
- The problem was that the icon I was trying to display doesn't exist on 10.13
However, more testing on 10.13 has revealed that the mechanism I was using to set up updates to install on logout no longer works, at least for some updates, on 10.13. In particular, the 10.13.2 (or 3) combo update is a 'staged install' which, currently, I can't find any way of setting up to install on logout. This is really frustrating, as I can't think of another, clean, way to 'force' the user into installing updates after a deferral limit. I guess it can be done with a logout policy but I can't think how we'd throw up a progress indicator in that case.
So, I'm kinda back to the drawing board on this one ... thanks for all the interest and advice!
For completeness, here's a version that almost, but not quite, works on 10.13:
https://github.com/UoE-macOS/jss/blob/10.13/coreconfig-softwareupdate-run.py
One interesting thing I noticed is that it's no longer possible to 'kill -HUP' the softwareupdate daemon or helper processes. I guess SIP now prevents it, but that's also a bit of a pain as that proved to be necessary in 10.12 to get the helper to notice when we downloaded updates.
Sigh. Methinks I may be 'fighting the system' a bit too much on this one.
This is really frustrating, as I can't think of another, clean, way to 'force' the user into installing updates after a deferral limit. I guess it can be done with a logout policy but I can't think how we'd throw up a progress indicator in that case.
What if the dialog box that we pop up once the deferral limit has been hit simply runs:
cmd_with_timeout([ SWUPDATE, '-i', '-a' ], 3600)
I'm not sure exactly how the behavior on this works when a reboot is required, does it just reboot? or prompt the user to choose to restart? If that's the case we're back to square one.
Hmm, or maybe once the deferral limit has hit, we start the installs, wait for them to finish, then pop a dialog with "a restart now to complete install" that's not optional with timeout if no answer?
Just sort of thinking out loud..
Thanks,
Matt
@mbezzo
Who is launching the python script?
I have similar code, but I'm using a flag file to determine how many deferrals are left before the command just runs to install this application we are deploying. I ask about who is running the script, because when JAMF runs the script, it's running as root so no need to sudo.
While a user is logged in, they still get the prompt without having to su as the user. Is that not happening for you? Do you have more code you can share that I could take a look at?
Hi @abrahamT,
I'm talking specifically about osascript - in my experience (I haven't actually retested in a couple years to be fair...) I've always had to run the osascript dialog creation as the currently logged in user to get the dialogs to show. Maybe that's no longer true? To answer your question specifically, the script is being run as root. I don't have any specific code other than what @glee_edin has provided - I haven't been able to dive back into this for the last couple of weeks sadly! Hope to soon.
If you're willing to share your code, I'd love to see it. I still haven't found precisely what I want yet - most things are too complex or too simple... I want it to be just right! ;)
Thanks,
Matt