Script to check if application is open and quit - using script parameter

May
Contributor III

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"'

375be4f9a1ce447c9dbd20ce5aa86c66

paging @mm2270 as i think i originally found the asuser approach from you !

10 REPLIES 10

pblake
Contributor III

Do you need to put a parameter in quotes?

May
Contributor III

@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)

dpertschi
Valued Contributor

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

May
Contributor III

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 !

tlarkin
Honored Contributor

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

tlarkin
Honored Contributor

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

jared_f
Valued Contributor

Cool script. But, wouldn't the restricted software work for this? It quits the app and sends the user a message.

tlarkin
Honored Contributor
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.

May
Contributor III

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"

tlarkin
Honored Contributor

@May

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