Posted on 07-16-2015 01:33 PM
Hi all
I'm trying to create an Extension Attribute to report back the status of a Global LaunchAgent which is in /Library/LaunchAgents
When run from the user context it reports back correctly but when run as root it does not, i've gone through most of the dicussions and tried adding in ls -l /dev/console | cut -d " " -f4 then sudo -u to the logged in user but my script fu is weak.
Can any one advise how to make the script below run the launchctl list command as the logged in user as an extension attribute ?
Cheers,
Andy
#!/bin/sh
agentloaded=`launchctl list | grep local.keepAlive.ADPassMon | awk '{print $NF}'`
if [ "${agentloaded}" == "local.keepAlive.ADPassMon" ]; then
echo "<result>Loaded</result>"
else
echo "<result>not loaded or installed</result>"
fi
Solved! Go to Solution.
Posted on 07-17-2015 10:19 AM
Here's my style of getting console user name (there's a one liner somewhere on jamfnation! : )
P.S. Backtick style not at as pretty as $( )
#!/bin/sh
#get uid of console owner
eval $(stat -s /dev/console)
#get username
consoleUsername=$(id -un $st_uid)
#grep for string as console user
agentloaded=$(su $consoleUsername -c "launchctl list | grep local.keepAlive.ADPassMon")
#if string is not empty
if [ -n "${agentloaded}" ]; then
echo "<result>Loaded</result>"
else
echo "<result>not loaded or installed</result>"
fi
Posted on 07-16-2015 01:42 PM
Try something like this. Not a guarantee to work, but for the moment at least it should. Things may change in the future though.
#!/bin/sh
loggedInUser=$( ls -l /dev/console | awk '{print $3}' )
loggedInPID=$( ps -axj | awk "/^$loggedInUser/ && /Dock.app/ {print $2;exit}" )
agentloaded=$(/bin/launchctl bsexec "${loggedInPID}" sudo -iu "${loggedInUser}" "launchctl list | grep local.keepAlive.ADPassMon | awk '{print $NF}'")
if [ "${agentloaded}" ]; then
echo "<result>Loaded</result>"
else
echo "<result>not loaded or installed</result>"
fi
Note there is no reason to check the grep output in the first if block I believe, since if grep locates the item in the launchctl list output, it results in a true state, meaning the if/then really only needs to see if the "$agentloaded" variable resulted in true (or not) to echo back the result you want.
Posted on 07-17-2015 10:19 AM
Here's my style of getting console user name (there's a one liner somewhere on jamfnation! : )
P.S. Backtick style not at as pretty as $( )
#!/bin/sh
#get uid of console owner
eval $(stat -s /dev/console)
#get username
consoleUsername=$(id -un $st_uid)
#grep for string as console user
agentloaded=$(su $consoleUsername -c "launchctl list | grep local.keepAlive.ADPassMon")
#if string is not empty
if [ -n "${agentloaded}" ]; then
echo "<result>Loaded</result>"
else
echo "<result>not loaded or installed</result>"
fi
Posted on 07-20-2015 07:50 AM
Thanks @mm2270 @yr_joelbruner for your help,
It made for a great end to the week when it all came together!
Posted on 07-21-2015 02:20 PM
I've applied your method to a script that loads the launch agent if required, what seems to be happening is that the script fails if the Mac is at the screensaver lock but runs happily if the it's not.
Could you advise a way to get around this ?
the script
#!/bin/sh
#get uid of console owner
eval $(stat -s /dev/console)
#get username
consoleUsername=$(id -un $st_uid)
#Load LaunchAgent as console user
(su $consoleUsername -c "launchctl load /Library/LaunchAgents/local.adpassmon.job.plist")
echo "loaded"
exit 0
when it fails the output is
Executing Policy ADMonPass re-load Launch Agent...
Running script ADMonPass Load LaunchAgent...
Script exit code: 0
Script result: Could not open job overrides database at: /private/var/db/launchd.db/com.apple.launchd/overrides.plist: 13: Permission denied
launch_msg(): Socket is not connected loaded
Posted on 07-21-2015 04:40 PM
@May I'm wondering why you are doing this for ADPassMon? I just use a LaunchAgent that loads at login. Maybe you have users who are unloading the LaunchAgent. You probably need a check to see if anyone owns the console and an if statement to exit gracefully if nobody owns it.
Posted on 07-22-2015 07:25 AM
Hi @jhbush1973
We need to make sure the application stays open regardless so we have the LaunchAgent set to keep alive, my main concern is that with the tiniest bit of Googling anyone can learn how to unload the launch agent and quit the app if they want.
I'm wondering if it's possible to use a Global LaunchDaemon rather than LaunchAgent to keep the application alive ? that way non admin users couldn't unload it and also if it didn't load it would be simpler to re-load through a policy as root.
Google time!
Posted on 07-22-2015 07:39 AM
I'm not sure if it would work to load it as a LaunchDaemon. I haven't used ADPassMon so I don't know for sure, but I think it needs to be run as the logged in user, hence why it was designed to be called by a LaunchAgent. But you're correct that launchctl unload can be run by any user to unload a global LaunchAgent, so its not a guarantee it will be running.
As for the error you ran into, have you tried using the launchctl bsexec method I mentioned above. Not certain it will get past the error or not, but worth a try.
Posted on 07-22-2015 07:48 AM
@May Hmmm, who knew screensaver would interfere with user launchd loading? Perhaps bsexec would get past that? Otherwise why not have ADPassMon load as a login policy? That works for me...
You could use the bits of the script above to detect if the user ID is above 1024 (or whatever threshold makes sense) so you know the user is an ActiveDirectory user and not local. Then use the su command to fire off an "open /path/to/ADPassMon.app" as the console user? That'll work.
And if they happen to quit it, well that's their darn fault! ;) You could always have a Self Service policy to open ADPassMon using the same console user detection routines and "open" as the console user, if ADPAssMon is squirreled away someplace non-obvious to the user (usually that's... beyond the Desktop :)
Posted on 07-22-2015 10:06 AM
Here is what I use for my launchagent which we put in /Library/LaunchAgents/ It makes sure that ADPassMon cannot be quit which sounds like what you're trying to achieve.
<?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>Label</key>
<string>com.company.adpassmon</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Utilities/ADPassMon.app/Contents/MacOS/ADPassMon</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
Not sure if it helps.
Posted on 07-22-2015 10:44 AM
@bpavlov Getting a LaunchAgent installed with KeepAlive is easy. Preventing users from locating and disabling that LaunchAgent is a different story.
A user (with the right knowledge) can run:
launchctl unload /Library/LaucnhAgents/com.company.addpassmon.plist
Note no 'sudo' required to unload it.
Posted on 07-22-2015 02:15 PM
@mm2270 @yr_joelbruner @bpavlov
It looks like it's not possible to get a LaunchDaemon to launch the application.
I think i've found the reason for the LaunchAgent occasionally not loading (blaming the screensaver was a late afternoon caffeine fuelled assumption!)
The LaunchAgent that i was using to keep the app alive was created by Launch Control, comparing @bpavlov's LaunchAgent i noticed the Launch Control created one uses /usr/sbin/open and the other uses the path to the binary.
The load command has been working 100% so far with the latter LaunchAgent, the one question i have is are there any implications with launching the application directly from the binary rather than the Application itself?
Thanks for all your input!
Posted on 07-22-2015 03:01 PM
@mm2270 Sure, that's true in a lot of situations. I'm not sure there's a better method that isn't too convoluted though.
@May A launch daemon runs as root whereas launch agents run as the user. LaunchDaemons will run immediately upon boot up and do not require someone to log in whereas LaunchAgents do. You shouldn't run into any problems running the application directly from the binary as far as I'm aware. I've been running it this way for quite a few months now. But perhaps @bentoms can provide more insight.
Posted on 07-23-2015 05:03 AM
@yr_joelbruner do you realise you can run
stat -f%Su /dev/console
instead of
eval $(stat -s /dev/console); id -un $st_uid
Posted on 07-23-2015 11:26 AM
@sean Like I said "(there's a one liner somewhere on jamfnation! : )" and like manna from heaven here it is... Very nice thanks gets rid of an unneeded eval
And to expound on what has been revealed:
The -f flag specifies formatted output, the capital S further specifies "string" output, without it the output is numerical
consoleUsername=$(stat -f %Su /dev/console)
consoleUserID=$(stat -f %u /dev/console)
To apply it all into script to run in a login policy (perhaps this could work for you @May?)
#!/bin/bash
consoleUserID=$(stat -f %u /dev/console)
#user ID is sufficiently high to denote Active Directory
if [ "$consoleUserID" -gt 1024 ]; then
consoleUsername=$(stat -f %Su /dev/console)
su $consoleUsername -c "open /Applications/ADPAssMon.app"
fi
Posted on 07-24-2015 10:28 AM
Finally got it working how i want using a combination of all your inputs, thank you!! @yr_joelbruner @mm2270 @bpavlov @sean
using this LaunchAgent (preferred this method as it played nicer with launchctl load and also launching via the binary quits the app when the agent is unloaded)
<?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>KeepAlive</key>
<true/>
<key>Label</key>
<string>local.keepAlive.ADPassMon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/open</string>
<string>-W</string>
<string>/Applications/ADPassMon.app</string>
</array>
</dict>
</plist>
This script to initially load it, then used in a policy to re-load it if neccessary (smart group based on EA at bottom)
#!/bin/sh
loggedInUser=$( stat -f%Su /dev/console )
loggedInPID=$( ps -axj | awk "/^$loggedInUser/ && /Dock.app/ {print $2;exit}" )
(/bin/launchctl bsexec "${loggedInPID}" sudo -iu "${loggedInUser}" "launchctl load /Library/LaunchAgents/local.adpassmon.job.plist")
Extension attribute to see if Agent is loaded
#!/bin/sh
loggedInUser=$( stat -f%Su /dev/console )
loggedInPID=$( ps -axj | awk "/^$loggedInUser/ && /Dock.app/ {print $2;exit}" )
agentloaded=$(/bin/launchctl bsexec "${loggedInPID}" sudo -iu "${loggedInUser}" "launchctl list | grep local.keepAlive.ADPassMon")
#if string is not empty
if [ -n "${agentloaded}" ]; then
echo "<result>Loaded</result>"
else
echo "<result>not loaded or installed</result>"
fi
Coffee time!
Posted on 08-07-2015 11:57 PM
FWIW, ADPassMon needs to be run as the user.
It has various calls to things like $USER which would be unhappy if ran via root.
It's great you've found a solution & that you're using ADPassMon :) just wanted to chime in on the user running the app.