Force logout help please!

Manu
New Contributor III

Hello,

I am sorry to bother everyone on this matter, but I have been working on an issue for a week and I haven't been able to find a solution to my problem.
I can feel I am not far, but there is one last thing in my way which prevent me from moving forward.

To make it short, we are about to push Sierra upgrade on all Mac using LANDesk (Ivanti). So far everything works well. I was able to write a script to bypass FV2, I also wrote a script which force quit all the Application in order to prevent a restart. But the last scenario that I am working on and which I am struggling is to Force log out any active user in a Mac except the current user.

Using the command line

sudo kill <pid>

With this command, I was able to force logout an active user of a test Mac, but to do so, I had to find the PID.
Now the difficulty is that in my company, we have many users, and I can't find the PID for each user on each computer manually.
So I need to write a script which will find the PID of all active user on a machine and force logout the active user except the current user.

So far, I have written this script: (This helps me to detect the other active users)

#!/bin/sh
CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CUser" | sed 's/console.*//' )

# If I run this command on a test machine where we have some test users:
infoPID=$( ps -Ajc | grep loginwindow | grep "$OUsers" )
echo "$infoPID"

# I get this result:
ld-imac-lab
fp.test1         1047   167  1047      0    0 Ss     ??    0:00.44 loginwindow
fp test4         1628   167  1628      0    0 Ss     ??    0:00.57 loginwindow
fp.test2         2094   167  2094      0    0 Ss     ??    0:00.38 loginwindow

This is where I am struggling. I need to be able to get only the PID:

1047
1628
2094

But because some users have a space in there logging name, I can't use "awk":

infoPID=$( ps -Ajc | grep loginwindow | grep "$OUsers" | awk '{print $2}' )

I can't use "cut" either:

infoPID=$( ps -Ajc | grep loginwindow | grep "$OUsers" | tr -s ' ' | cut -d' ' -f2 )

So if someone could help me get this PID by adding a command line it would be very much appreciate.
Right now, it works perfectly on the test computer when I don't have the account which has . space in the login name:

CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CUser" | sed 's/console.*//' )

ps -Ajc | grep loginwindow | grep "$OUsers" | awk '{print $2}' | sudo xargs kill -9
1 ACCEPTED SOLUTION

Manu
New Contributor III

Hello everyone,

I am updating this post because I have found a solution that works for me. It may not be the best one, but it does what i want: Force log out other active users.

The first step, it with the help of @mm2270 I was able to create a script that logs out one active user 9Usually the last one who logged in before the current user.

#!/bin/sh
#
# Step 1: Log out Active User command.

CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CurrentUser" | sed 's/console.*//' )
loggedInUID=$( echo "$OtherActiveUsers" | while read userName; do
    id -u "$userName" 
    done )


echo "$OUsers" | while read userName; do
echo "$loggedInUID" | xargs /bin/launchctl asuser "$loggedInUID" sudo -iu "$userName" "/usr/bin/osascript -e 'tell application "loginwindow" to  «event aevtrlgo»'"
done

exit 0

Unfortunately, at this point, I wasn't able to repeat my command. So I decided to create a second script which will repeat the first script according to the amount of other active users that are logged in.

#!/bin/sh
#
# Step 2: Log out Active User command.
# Note: I have named my first script Force_Log_Out_part_1.sh and with PackageMaker, it copies the script on the /tmp/ folder.

CUser=$(stat -f%Su /dev/console)
OUsers=$( who | grep -v _mbsetupuser | grep -v ttys | grep -v "$CUser" | sed 's/console.*//' )
AUsers=$( who | grep -v _mbsetupuser | grep -v ttys | sed 's/console.*//' )
NumberOUsers=$( echo "$OUsers" |  wc -l | sed 's/ //g' )
NumberAUsers=$( echo "$AUsers" |  wc -l | sed 's/ //g' )

if [ "$NumberOUsers" -ge 1 ] ; then   
    for n in $(seq $NumberAUsers); do
    sleep 3 
    sh /tmp/Force_Log_Out_part_1.1.sh
    wait
    done
    else
        sleep 5
        rm /tmp/Force_Log_Out_part_1.sh
fi

exit 0

Now it is a bit messy, and I am aware that there is a lot of "sleep" and "wait", but I think it is to let the process loginwindow to restart. Otherwise you get some error message saying that the loginwindow is not running.
Nevertheless, as I said, it does what I want. If there is not other active user, it doesn't do much. And if there is, then it Force log them out, and it doesn't matter how many there are.

View solution in original post

6 REPLIES 6

mm2270
Legendary Contributor III

There's a better way to do this using launchctl. Try this:

#!/bin/bash

loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")

/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/usr/bin/osascript -e 'tell app "loginwindow" to «event aevtrlgo»"

In case you're wondering, this tells the script to run the Applescript event command aevtrlgo (which stands for applescript event really log out) It will quickly force a logout, in most cases ignoring open documents but it's best to ensure all applications are shut down prior to this to be sure nothing blocks the log out. Apps that support resume shouldn't interfere as they will pop back up on next login to that account, back to where they were.

I can't say I've tested the above on Sierra, so your mileage may vary with it, but give it a try as this is much more graceful than trying to kill the loginwindow process. It also works well with deferred FileVault enablement. It should pop up the password dialog for them to enable FileVault, in case that was something you needed to do as well.

CapU
Contributor III

This always works for me:

ps -Ajc | grep loginwindow | awk '{print $2}' | sudo xargs kill -9

Manu
New Contributor III

Hello,

@mm2270
Thank you for your help. I tried to run your script with ARD on my test iMac and it came up with an error. But then, after looking at it, I noticed that it was missing a ' at the end of the command line.
So I tried it again, and it works perfectly if I want to log out the current the user.
But here is the thing: as the upgrade from 10.10.5 to 10.12.6 is happening on the current user, I don't want to log out this account.
In my company, we have a lot of freelancers, and some of them log in to the same Mac and don't bother login out. So sometime, you have 3 or 4 accounts "active" on the same machine.
So when you restart a computer, there is a pop window telling you that there are other account active on the machine and it require an Admin password to restart the machine.
So if I can "Force log out" those active accounts, then during the upgrade, the computer will restart without any issue.

I tried to modify your script to my need:

CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CurrentUser" | sed 's/console.*//' )
loggedInUID=$( echo "$OtherActiveUsers" | while read userName; do
    id -u "$userName" 
    done )

/bin/launchctl asuser "$loggedInUID" sudo -iu "$OtherActiveUsers" "/usr/bin/osascript -e 'tell application "loginwindow" to  «event aevtrlgo»'"

But that didn't work either. It thinks that my list of "Other Active Users" is just one user.

If modify my command line to

xargs /bin/launchctl asuser "$loggedInUID" sudo -iu "$OUsers" "/usr/bin/osascript -e 'tell application "loginwindow" to  «event aevtrlgo»'"

Then nothing happens...

@CapU Thank you for your help on the issue I am having. That was indeed an option I was looking at, and it works great if no user have a space in their login name.
This is the command line I used:

CUser=$(stat -f%Su /dev/console)
OUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CUser" | sed 's/console.*//' )

ps -Ajc | grep loginwindow | grep "$OUsers" | awk '{print $2}' | sudo xargs kill -9

But in my company, we have some users with a space in there login name, and on my test iMac, I have the following account: fp test4

That's why I need to find a way to get the PID column only by using something else than awk or cut.
from this:

ld-imac-lab fp.test1 1047 167 1047 0 0 Ss ?? 0:00.44 loginwindow fp test4 1628 167 1628 0 0 Ss ?? 0:00.57 loginwindow fp.test2 2094 167 2094 0 0 Ss ?? 0:00.38 loginwindow

to this :

ld-imac-lab 1047 1628 2094

Manu
New Contributor III

Update!!

I was able to "Force Log Out" one of the "Other Active User":

CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CurrentUser" | sed 's/console.*//' )
loggedInUID=$( echo "$OtherActiveUsers" | while read userName; do
    id -u "$userName" 
    done )
echo "$OtherActiveUsers" | while read userName; do
echo "$loggedInUID" | xargs /bin/launchctl asuser "$loggedInUID" sudo -iu "$userName" "/usr/bin/osascript -e 'tell application "loginwindow" to  «event aevtrlgo»'"
done

I assume it logs out the first user on the list. So all I have to do now is create a command that will detect if there is an other active user, log him/her out and repeat until there isn't any left...

Thank you all for your help, I feel I am moving forward again and I am not far from finding the solution.

Manu
New Contributor III

Hello everyone,

I am updating this post because I have found a solution that works for me. It may not be the best one, but it does what i want: Force log out other active users.

The first step, it with the help of @mm2270 I was able to create a script that logs out one active user 9Usually the last one who logged in before the current user.

#!/bin/sh
#
# Step 1: Log out Active User command.

CurrentUser=$(stat -f%Su /dev/console)
OtherActiveUsers=$( who | grep -v _mbsetupuser | grep -v ttys000 | grep -v "$CurrentUser" | sed 's/console.*//' )
loggedInUID=$( echo "$OtherActiveUsers" | while read userName; do
    id -u "$userName" 
    done )


echo "$OUsers" | while read userName; do
echo "$loggedInUID" | xargs /bin/launchctl asuser "$loggedInUID" sudo -iu "$userName" "/usr/bin/osascript -e 'tell application "loginwindow" to  «event aevtrlgo»'"
done

exit 0

Unfortunately, at this point, I wasn't able to repeat my command. So I decided to create a second script which will repeat the first script according to the amount of other active users that are logged in.

#!/bin/sh
#
# Step 2: Log out Active User command.
# Note: I have named my first script Force_Log_Out_part_1.sh and with PackageMaker, it copies the script on the /tmp/ folder.

CUser=$(stat -f%Su /dev/console)
OUsers=$( who | grep -v _mbsetupuser | grep -v ttys | grep -v "$CUser" | sed 's/console.*//' )
AUsers=$( who | grep -v _mbsetupuser | grep -v ttys | sed 's/console.*//' )
NumberOUsers=$( echo "$OUsers" |  wc -l | sed 's/ //g' )
NumberAUsers=$( echo "$AUsers" |  wc -l | sed 's/ //g' )

if [ "$NumberOUsers" -ge 1 ] ; then   
    for n in $(seq $NumberAUsers); do
    sleep 3 
    sh /tmp/Force_Log_Out_part_1.1.sh
    wait
    done
    else
        sleep 5
        rm /tmp/Force_Log_Out_part_1.sh
fi

exit 0

Now it is a bit messy, and I am aware that there is a lot of "sleep" and "wait", but I think it is to let the process loginwindow to restart. Otherwise you get some error message saying that the loginwindow is not running.
Nevertheless, as I said, it does what I want. If there is not other active user, it doesn't do much. And if there is, then it Force log them out, and it doesn't matter how many there are.

druocco
New Contributor III

@Manu Thanks for posting this one - It's the first script I was able to run without resulting in error.
Although, I am still not successfully getting a forced logout after testing it via Self Service.

I get this result in the logs:
Script result: id: : no such user
Script result: sh: /tmp/Force_Log_Out_part_1.1.sh: No such file or directory

Apologies as I am a novice in bash and absolutely can be missing something obvious.