Posted on 01-22-2018 08:00 AM
Hi All,
Hoping someone has already solved this problem and is willing to share! I'm reworking a great python script from @glee_edin (thanks!) (GitHub link) and need to run osascript as the logged in user. I've never done anything in python before, so I'm coming up a little short. I've got the function looking like this so far: (ignore the bad quoting - the forum isn't parsing it correctly)
#!/usr/bin/python
def user_wants_to_defer(defer_until, updates):
answer = """
osascript -e '
tell application "System Events"
with timeout of 8947878 seconds
set result to (display dialog "The following updates are available and require a restart:
%s
You can choose to restart and install them now, or delay until: %s
Updates will automatically be installed at that time if they haven't already been installed." with title "Required macOS Updates Available" with icon ("path/to/icon.icns" as POSIX file) buttons {"Restart Later", "Restart Now"} default button "Restart Now")
end timeout
end tell
if button returned of result = "Restart Later" then
return "2"
else
return "0"
end if
'
""" % (updates, defer_until.strftime("%H:%M on %a, %d %b"))
in bash I'd just do this:
#!/bin/sh
su - "${loggedInUser}" -c osascript -e <<EOT
do stuff here
EOT
Anybody know of something similar? Hopefully this makes sense... :)
Thanks!
att
Posted on 01-22-2018 08:06 AM
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)
Posted on 01-22-2018 08:08 AM
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!
Posted on 01-22-2018 08:11 AM
Awesome, thank you!
Posted on 01-22-2018 12:32 PM
@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.
Posted on 01-22-2018 02:48 PM
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
Posted on 01-23-2018 02:00 AM
@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
Posted on 01-23-2018 07:27 AM
Oooo, awesome!!! THANK YOU! Hoping to spend some time on this today. :)
Posted on 01-23-2018 08:50 AM
@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
Posted on 01-23-2018 09:44 AM
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.
Posted on 01-23-2018 09:48 AM
Sounds good - REALLY appreciate your work on this!
Posted on 01-23-2018 09:51 AM
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.
Posted on 01-23-2018 09:53 AM
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.
Posted on 01-23-2018 02:25 PM
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
Posted on 01-24-2018 12:42 AM
Thanks all - just to update the thread:
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!
Posted on 01-24-2018 01:19 AM
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.
Posted on 01-25-2018 09:08 AM
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
Posted on 02-05-2018 03:52 PM
@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?
Posted on 02-06-2018 08:09 AM
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