Posted on 08-24-2017 08:34 AM
Hi all
I'm trying to make a script that can use a parameter to check if an application is open, notify the user if it is, then quit the application, i've got the check and notify working with a parameter but i'm unable to get the quit app part working.
if i use the application name rather than the parameter the application quits ok, when using the parameter it does not, any pointers ?
works ok
/bin/launchctl asuser "$LoggedInUID" /usr/bin/osascript -e 'quit app "Microsoft Teams"'
does not work when using the Parameter
echo "Closing $4"
/bin/launchctl asuser "$LoggedInUID" /usr/bin/osascript -e 'quit app "$4"'
paging @mm2270 as i think i originally found the asuser approach from you !
Posted on 08-24-2017 08:36 AM
Do you need to put a parameter in quotes?
Posted on 08-24-2017 08:44 AM
@pblake thanks,
i just tried it without
echo "Closing $4"
/bin/launchctl asuser "$LoggedInUID" /usr/bin/osascript -e 'quit app $4'
and get this error
9:10: syntax error: Expected expression, property or key form, etc. but found unknown token. (-2741)
Posted on 08-24-2017 09:00 AM
If you want to nag the user to death, to quit the app clean, check out what Mike did here:
https://jamfnation.jamfsoftware.com/discussion.html?id=19774#responseChild119167
Posted on 08-24-2017 09:36 AM
Thanks @dpertschi
That's a good idea ! keep looping until they quit the application, ideally i want to keep this as simple for the user as possible, if i can't get it working then i'll go for the nag approach !
Posted on 08-25-2017 03:24 PM
Started work early so left work early today, and still wanted to write code. Trying to get better PyObjC chops and this seems to work on my system locally but I have not put it up on the JSS at all yet.
#!/usr/bin/python
# proof of concept code use at own risk, test this before using it
# by tlarkin
from Cocoa import NSRunningApplication
from AppKit import NSWorkspace
import sys
kill_list = sys.argv[4].split(',')
def get_running_apps():
proc_dict = {}
workspace = NSWorkspace.sharedWorkspace()
running_apps = workspace.runningApplications()
for app in running_apps:
proc_name = app.localizedName()
pid = app.processIdentifier()
proc_dict[proc_name] = pid
return proc_dict
def get_pids(kill_list, running_apps):
pid_list =[]
for app, pid in running_apps.items():
if app in kill_list:
print 'found %s app with PID %s' % (app, pid)
pid_list.append(pid)
return pid_list
def quit_application(pids):
for pid in pids:
pid = int(pid)
app = NSRunningApplication.runningApplicationWithProcessIdentifier_(pid)
app.terminate()
def run():
apps = get_running_apps()
pid_list = get_pids(kill_list, apps)
quit_application(pid_list)
if __name__=='__main__':
run()
You can use positional parameter 4 to add a comma delimited string and it will quit all those apps.
example output:
bash-3.2# whoami
root
bash-3.2# python quitprocess.py none none none Messages,TextEdit,FakeApp
found TextEdit app with PID 6830
found Messages app with PID 6829
Tested on a 10.12.5 macOS
So in the JSS you could try under parameter 4, AppName1,AppName2,AppName3
with no spaces, comma delimited. Also please test this out before deploying it. It doesn't force quit the app either, so if the app is open it will prompt the user to save or cancel.
Hope this helps,
Tom
Posted on 08-25-2017 04:33 PM
Also if your App has spaces in the name include every app in quotes like so:
"Messages,TextEdit,FakeApp,App Store"
example output:
python quitprocess.py none none none "Messages,TextEdit,FakeApp,App Store"
found TextEdit app with PID 7097
found Messages app with PID 7091
found App Store app with PID 7092
Posted on 08-25-2017 09:18 PM
Cool script. But, wouldn't the restricted software work for this? It quits the app and sends the user a message.
Posted on 08-25-2017 09:26 PM
Cool script. But, wouldn't the restricted software work for this? It quits the app and sends the user a message.
@jared_f Restricted Software would require a management framework update, and you probably don't want to restrict it, then un-restrict it. Plus the management framework is global, so scoping that would be a huge pain. I am not saying it couldn't work, but rather it would be a lot of work.
The only thing missing would be some sort of dialog box pop up from my code but you can use NSUserNotificationCenter
from PyObjC, or mess around with Tkinter
which is a UI module for Python.
Posted on 08-28-2017 09:46 AM
Thanks @tlarkin
I couldn't get your script to work from the JSS, with "Microsoft Teams" as the $4 parameter, as a test if i run the script locally
(changing kill_list = "Microsoft Teams") it works, i think there's something going on with pulling that parameter when running the command as a user.
I've managed to get my script working by creating a separate script from within the main one, the script is created on the fly and then run as the user, this pulls the parameter from the JSS and quits the application successfully, on the JSS it needs to be set without quotes, i pinched the idea from @mm2270 here
thanks again for you help!
Here's my convoluted yet working script, checks if the application set in parameter $4 is open, notifies user and gives the option to quit the app then trigger the update policy or exit, if the app is closed it silently triggers the update.
#!/bin/bash
#check if app is open, notify and quit, use script parameter for application name
LoggedInUser=$( stat -f%Su /dev/console )
LoggedInUID=$(stat -f %u /dev/console)
if [[ $LoggedInUser = "root" ]]; then
echo "No user logged in - exiting script"
exit 0
fi
# check to see if a value was passed for $4, and if so, assign it
if [ "$4" != "" ]; then
application=$4
else
echo "No application parameter set on JSS, exiting script"
exit 1
fi
#convert to add .app and brackets - re https://www.jamf.com/jamf-nation/discussions/22283/check-if-program-is-running-script-using-ps-aux-grep
application=$( echo "$4".app | sed 's/./&]/1' | sed -e 's/^/[/' )
#echo "converted string = $application"
number=$(ps ax | grep -c "$application")
if [ $number -gt 0 ]; then
echo "$4 is open - notify user"
#Notify
cp /Library/Application Support/JAMF/bin/icons/Notifications.icns /private/var/tmp
sleep 3
icon="/private/var/tmp/Notifications.icns"
title="IT Notification - Update"
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
description="$4 needs to be quit so it
can be updated, please click QUIT to
close the application and update now
or choose LATER to update later"
button1="LATER"
button2="UPDATE"
#resolved pasteboard error - https://www.jamf.com/jamf-nation/discussions/17245/running-management-action-fails-on-10-10-and-10-11
userChoice=$(/bin/launchctl asuser $(id -u $LoggedInUser) sudo -u $(ls -l /dev/console | awk '{print $3}') "$jamfHelper" -windowPosition ul -windowType hud -description "$description" -title "$title" -button1 "$button1" -button2 "$button2" -icon "$icon")
if [ "$userChoice" == "2" ]; then
echo "User clicked UPDATE - hoorah"
rm /private/var/tmp/Notifications.icns
#quit application
#---- create separate script to run as user, cannot get asuser working with a $ parameter
#approach from - https://www.jamf.com/jamf-nation/discussions/24584/need-help-forcing-script-to-run-commands-under-current-logged-on-user
cat << EOF > /private/tmp/quit_application.sh
#!/bin/bash
echo "Closing $4"
/usr/bin/osascript -e 'quit app "$4"'
EOF
if [ -e /private/tmp/quit_application.sh ]; then
/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$LoggedInUID" sudo -iu "$LoggedInUser" "/private/tmp/quit_application.sh"
sleep 2
echo "Cleaning up..."
/bin/rm -f "/private/tmp/quit_application.sh"
else
echo "Oops! Couldn't find the script to run. Something went wrong!"
exit 1
fi
#convert $4 paramater so it can be used in trigger command, remove space and make lower case
trigger=$( echo $4 | sed 's/ //g' | tr '[:upper:]' '[:lower:]' )
echo "triggering policy"
jamf policy -trigger "run$trigger"
exit 0
else
echo "User clicked later - exit script"
rm /private/var/tmp/Notifications.icns
exit 0
fi
fi
echo "$4 is closed - triggering policy"
trigger=$( echo $4 | sed 's/ //g' | tr '[:upper:]' '[:lower:]' )
jamf policy -trigger "run$trigger"
Posted on 08-28-2017 10:06 AM
It could be because jamf always passes the first 3 parameters in every script so you might need to add this
boot_drive = sys.argv[1]
computer_name = sys.argv[2]
username = sys.argv[3]
kill_list = sys.argv[4].split(',')
You can also try running it with sudo jamf runscript -script script_name.py -path /path/to/script -target / -p4 "Microsoft Teams"
to see if that makes any difference. I know there are issues passing parameters sometimes since Jamf always passes all 11 parameters even if you do not use them in a script