Posted on 10-30-2015 03:23 PM
Good afternoon fellow geeks
We need to move away from login/logout hooks. I have been researching using launchd and think I have a good start for using launchd to run a script but now I want to run something when the user logs out ( I want to un-mount some network shares)
Does anyone have any ideas?
Thanks in advance
Posted on 10-30-2015 03:34 PM
Posted on 10-30-2015 05:05 PM
We've implemented a logout watcher with a LaunchAgent for cleaning user directories on shared Macs, but you can easily modify the following examples to do $thingyouneed
.
LaunchAgent: https://gist.github.com/bruienne/7fb612c413216ccea86b
Logout watch script: https://gist.github.com/bruienne/7fb612c413216ccea86b
Your script should go inside the onLogout()
function.
Posted on 10-30-2015 07:38 PM
I asked the same question a few years ago and there was no answers to you are not going to find a lot of options... : )
C
Posted on 10-31-2015 07:10 AM
One of the easiest ways to run a LogOUT script is via the logout hook..
( - which is not using Launchd.. )
sudo defaults write com.apple.loginwindow logoutHook path/to/my/logoutscript.sh
But before you do that use:
sudo defaults read com.apple.loginwindow
and look to see what if any logout hooks are already defined..
- you will need to continue supporting those functions too
for instance JAMF have a logout script
You could do:
MyLogout_Script.sh My_Logout_Stuff JAMF_Logout
So that any JAMF logout items are still called at user logout
Posted on 10-31-2015 07:23 AM
I have used a LaunchDaemon, to run a system startup script.
One of whose tasks, is to examine the Login and logout hooks, an exact a repair of them if they have been disturbed by some other install, so that they are returned back to a known state.
I would guess that would be the main reason why you want to "move away from using a logout hook" ?
So although that also does not exactly answer your question..
But I think is a helpful contribution to this area of discussion, and shows one resolution of the issue.
LaunchDaemons don't seem to offer logout options, least not that I have spotted so far..
Posted on 10-31-2015 09:03 AM
I'm probably uninformed, but is there a reason not to use Casper's login/logout hooks? Why are you transitioning away – is it specific to your environment or a philosophical choice?
Posted on 10-31-2015 12:40 PM
@PeterClarke Great tip. i am not using loginhooks now , but good to know, and the comment about the state of the file, just gave me an idea with puppet
fantastic!
LSinNY
Posted on 10-31-2015 02:05 PM
Casper already places login and logout hooks on a system. All you need to do is create a policy that triggers at Logout and maintain that in your JSS.
Posted on 11-01-2015 05:14 AM
We have had people with admin access manually install things that have as a side effect blown-away the login and logout hooks, so now i detect that condition, and repair the login and logout hooks..
Without that JAMF Logout script would otherwise no longer be called..
It's a bit of an odd case - but that's what you get when you work in education..
People sometimes installing things that could potentially break the system..
Posted on 11-01-2015 03:20 PM
I don't know why people in this thread have completely missed the logout watcher launch agent that bruienne posted earlier...
It does the job the OP asked for. Run a script at logout, via a launchdaemon.
In case you missed it
Posted: 10/30/15 at 7:05 PM by Bruienne We've implemented a logout watcher with a LaunchAgent for cleaning user directories on shared Macs, but you can easily modify the following examples to do $thingyouneed. LaunchAgent: https://gist.github.com/bruienne/7fb612c413216ccea86b Logout watch script: https://gist.github.com/bruienne/7fb612c413216ccea86b Your script should go inside the onLogout() function.
Posted on 09-07-2016 06:56 AM
@Bruienne does your solution run successfully at logout if the machine is shut down? I'm imagining in this scenario, the system may go down before the script has finished executing, leaving the machine in a potentially unknown state. Is this actually the case?
Posted on 03-06-2017 11:28 AM
@Bruienne Looking to use your method but I can't find a copy of your logout watch script. Can you point me in the right direction please?
Posted on 05-31-2017 06:46 AM
Posted on 09-08-2017 02:14 PM
The one problem with the logoutwatcher.sh script is that a long process (e.g. resetting a common student directory by copying over a lot of files) will not finish before the logout process kills the script. the work around is a two step process:
# Script to touch a file when the student user logs out
# This file is watched by a launchdaemon that then resets the student user
# 170908-ski: initial version
onLogout() {
#Redirect STDOUT and STDERR to the desired logfile
exec > /tmp/nsd.studentlogout.log 2>&1
#Compare command to find if logouthook.sh file exists
if [ -e /etc/nsd/files/runlogoutwatcher ]; then
echo "Touching file to trigger launchdaemon"
touch /tmp/studentloggedout
echo "Done!"
fi
exit
}
echo "$(date): Watching for student logout. Waiting..."
trap 'onLogout' SIGINT SIGHUP SIGTERM
while true; do
sleep 86400 &
wait $!
done
Then set up a LaunchDaemon to look for changes to the /tmp file and run whenever it sees a change. Since Launch daemons are not dependent on the logout process they are not time limited:
<?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>org.nsd.studentsync.plist</string>
<key>ProgramArguments</key>
<array>
<string>/etc/nsd/files/studentsync.sh</string>
</array>
<key>WatchPaths</key>
<array>
<string>/tmp/studentloggedout</string>
</array>
</dict>
</plist>
Posted on 09-29-2018 11:35 AM
Works on shutdown but not logout
Posted on 01-21-2020 08:03 AM
Does the latest method posted by ski still work in Mojave? It seems like launchd stops watching /tmp/studentloggedout after shutdown/restart has begun. touch /tmp/studentloggedout seems to trigger the script only before a shutdown/restart.
Posted on 03-17-2021 09:55 AM
Knowing that Login/Logout Hooks would eventually disappear, I pivoted to using LaunchAgents years ago to cover login actions, but have been watching and waiting for an "official" replacement for the Logout Hook. When the Jamf Pro interface started putting a warning icon next to my policies that use a Logout trigger, i.e. that rely on a Logout Hook, I started looking for an alternative, which brought me to this thread.
I tried the method posted here by ski in macOS 10.15 Catalina, but couldn't get it to work, so I came up with my own scheme, and it seems to be working pretty well so far; I'm still testing it. It's a LaunchDaemon that runs a bash script that continuously parses the output of the 'last' command, and takes action when it detects that a user has logged out. The caveats are: (1) Because it's a LaunchDaemon, it runs with root permissions. (2) As written, it won't work if more than one user is logged in at a time (via Fast User Switching), but a better script-writer could probably make that work.
Here's the gist. Can anyone think of any reasons why this is a bad idea? Would any script-writers care to pilfer and improve?:
echo "Watching and waiting for a user to logout..."
while true ; do
# Case 1: If the first line of 'last' contains "reboot" and the second line contains "crash",
# then assume the username on the "crash" line inelegantly logged out:
myTest0=$( last -2 )
myTest0Line1=$( echo "${myTest0}" | sed -n '1 p' | grep -i "reboot")
myTest0Line2=$( echo "${myTest0}" | sed -n '2 p' | grep -i "crash")
myUser0=$( echo "${myTest0Line2}" | awk -F " " '{ print $1 }' )
# Case 2: Compare the most recent line of 'last' to the same line one second later.
# If the user names are the same and the status has changed from "still logged in"
# to a login duration "(HH:MM)", then assume the user just logged out:
myTest1=$( last -1 -t console )
myUser1=$( echo "${myTest1}" | awk -F " " '{ print $1 }' )
sleep 1
myTest2=$( last -1 -t console )
myUser2=$( echo "${myTest2}" | awk -F " " '{ print $1 }' )
# Case 2 Check:
if [[ $( echo "${myTest1}" | grep -i "still logged in" ) ]] &&
[[ $( echo "${myTest2}" | grep -i "(.*:.*)" ) ]] &&
[[ "${myUser1}" = "${myUser2}" ]] ; then
targetUser="${myUser2}"
reason="logout"
# Case 1 Check:
elif [[ "${myTest0Line1}" ]] && [[ "${myTest0Line2}" ]] ; then
targetUser="${myUser0}"
reason="crash"
else
targetUser=""
fi
# Take action:
if [[ "${targetUser}" ]] ; then
echo "${targetUser} logged out. [Reason: ${reason}]"
# Logout code goes here
fi
done