Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

timshead

macrumors newbie
Original poster
Aug 31, 2008
6
0
Let me explain what I'm trying to do before I get to my question. I'm trying to create a shell script for my Mac, OS 10.5.4, that does the following:

-Runs every 20 seconds
-Checks to see if a user is logged in and active
-Captures a screenshot of that user's desktop and saves it to disk

I figure out how to make the script run every 20 seconds by using launchctl along with a plist file. The plist file uses the StartInterval key with a value of 20, thus invoking it every 20 seconds. I loaded the job as root with the command "sudo launchtl load $path_to_plist_file", then "sudo load $name_of_job".

I figured out how to capture a screenshot with a script. Since the script is running AS root but it is not grabbing a screenshot FOR root, according to the screencapture man page I have to use the command "sudo launchctl bsexec $PID screencapture..." I have tested this and it works, but it only works if I know the PID for the current, active user.

I do not know how to find the PID for the current, active user.

As I said above, I would like the script to see first of all IF there is a user logged in with their desktop open. Since I have Fast User Switching enabled, there can be multiple users logged in but not active and the monitor displaying the login window. Also, the script is running as root so I can't use "whoami" or "logname" because they won't tell me anything about which user is using their desktop.

Does anyone know how a script can check to see if a user is logged in with their desktop active on the monitor?
 
I wanted to do the same thing a while back (it's a custom script that checks a server to determine if the laptop is stolen, and sends data if it is), and I ended up solving it using a little AppleScript magic. I didn't even think to look at the screencapture man page. Silly me. :rolleyes:

Basically, I created an AppleScript with the following contents:

Code:
set cmd to "/usr/sbin/screencapture -x -tjpg /tmp/screen.jpg > /dev/null 2>&1"
do shell script cmd

Of course you can change the screencapture options and the path of the resulting image file if you want. In Script Editor, save this as an application bundle, with none of the options checked.

You probably want to make it a background application so the user doesn't notice the script launching. To do this, go to the application bundle in Finder, right-click and Show Package Contents, then go to Contents and open Info.plist with TextEdit. Just above the line with <key>WindowState</key> insert the following:

Code:
	<key>NSUIElement</key>
	<string>1</string>

Finally, in your main shell script, call this with "open /path/to/AppleScriptBundle.app". This opens it as the current logged-in user without you having to guess who that is. ;)

Hopefully this helps. I suppose the only thing it doesn't do is check whether somebody is actually active on the machine.
 
Finally, in your main shell script, call this with "open /path/to/AppleScriptBundle.app". This opens it as the current logged-in user without you having to guess who that is. ;)

Hopefully this helps. I suppose the only thing it doesn't do is check whether somebody is actually active on the machine.

But if I'm running a shell script using launchctl, then it will not in fact open it as the current logged-in user, it will run it as root because I initiated it with the command "launchctl start $job_name".

But that gives me an idea; can I set launchctl to run the script as the current logged-in user?

Thanks for the idea.
 
I think what I'm trying to do may not be entirely possible. Here's a couple of blurbs from Apple's document "Supporting Fast User Switching" at http://developer.apple.com/documentation/MacOSX/Conceptual/BPMultipleUsers/Concepts/FastUserSwitching.html

Fast user switching lets users share a single machine without having to log out every time they want to access their user account. Users share physical access to the machine, including the same keyboard, mouse, and monitor. However, instead of logging out, a new user simply logs in and switches out the previous user.

Processes in a switched-out login session continue running as before. They can continue processing data, communicating with the system, and drawing to the screen buffer as before. However, because they are switched out, they do not receive input from the keyboard and mouse. Similarly, if they were to check, the monitor would appear to be in sleep mode. As a result, it may benefit some applications to adjust their behavior while in a switched-out state to avoid wasting system resources.

Obtaining shared system resources, such as TCP/IP ports, introduces other problems in a fast user switching environment. For example, if you have a chat connection, do you hold onto the port or give it up when a new user is switched in? How you handle these situations depends on the resource involved and the design of your application. Regardless of how you handle it, you need to know when the user switch occurs, and for that you should read the section “User Switch Notifications,” which describes the notifications available to applications.

Using the Core Graphics Framework
The Core Graphics framework relies on the presence of the window server and thus is available only to applications running in a login session. This framework includes the CGSessionCopyCurrentDictionary function for getting information about the current login session, including the user ID and name.

...and finally, from "User Switch Notifications"

When a user switch occurs, Mac OS X generates events for all interested applications. Events are sent to applications in a login session whenever the login session is activated or deactivated.

So I think what happens is loginwindow handles logging a user in, launching their dock and finder, etc. If a user switch occurs, an event is sent out to all apps in the login session that is deactivated and to the apps in the login session that is activated.

I think I'd have to create an actual application that starts up for each user when they login, accesses the Core Graphics Framework to get their username, and registers to receive the activation/deactivation events so it only generates screenshots while the user is active.

I am going to go another route based on what I've figured out. I am going to make my script run for each user when they login, then continue to run in the background no matter if they are active or not. I will make the script limit the number of screenshots it captures and I will make it store each user's screenshots in a separate folder.

If I am wrong about what I said above and it turns out there IS some way to do what I was trying to do, by all means let me know, but I think this will prove to be the easiest way to make my script work.
 
But if I'm running a shell script using launchctl, then it will not in fact open it as the current logged-in user, it will run it as root because I initiated it with the command "launchctl start $job_name".

My script runs from cron, which in OS X is effectively called by launchctl. I'm running it as root. When root runs the "open" command, the resulting application is run as the currently logged-in user. You can try it: just ssh in to the machine and run something like "sudo open /Applications/iTunes.app". Even though "open" runs as root, iTunes will start as the logged-in user. This is exactly the situation you want for running screencapture.

I wouldn't have suggested that solution if it didn't work for me. ;)
 
My bad. I see that your method does indeed work. It seems to use more system resources to launch the application; even though it is launching in the background I can tell *something* is going on, whereas with the script I couldn't tell that something was going on.

I think I am still going to try my second option, which is to use launchctl to run the script for each user upon login and continue to run until the user logs out. I'll post an update if/when I get this working.

My one concern with doing this via launchctl is that if the computer is put to sleep the launchctl events are queued up and run sequentially when the computer is turned back on. That could lead to a pretty severe performance hit if the computer is put to sleep for any period of time. I guess I'll just have to see what kind of impact it has.
 
I do not know how to find the PID for the current, active user.

As I said above, I would like the script to see first of all IF there is a user logged in with their desktop open. Since I have Fast User Switching enabled, there can be multiple users logged in but not active and the monitor displaying the login window. Also, the script is running as root so I can't use "whoami" or "logname" because they won't tell me anything about which user is using their desktop.

Does anyone know how a script can check to see if a user is logged in with their desktop active on the monitor?

$ man who
 
$ man who

Wow, so helpful. I read that, I don't think you read my question. If you did, perhaps you know how to use "who" to do what I'm trying to do. "Who" doesn't tell you which user is active, just which users are logged in.
 
My apologies for digging up an old thread, but since I was unable to find a suitable solution (here and elsewhere), I threw together a solution. Since it's possible that others still might come across this thread, I figured that adding it here might be helpful.

Anyhow… here's the solution I quickly threw together. It's just a small utility called activeUser that's based on Apple's developer documentation.

So, the SystemConfiguration framework provided by Apple gives applications access to the current "Console User" via a C call to SCDynamicStoreCopyConsoleUser. Other methods like who(1) and last(1) give lots of info about who might be logged in, but they don't specify which user is actually active. Active being defined as the currently logged in user. A user will be considered the current console user even if the screen saver is active. To further clarify, if a user has previously logged in, but has been "fast user switched" to the background, then that user is not the current console user.

All it does it get what Apple calls the "current console user", and returns the short username for that user, of if at the login screen it returns none. (Which could be a problem if you have a user named none, but if that's the case you might have bigger issues.)

Tested only on Mac OS X 10.6, but should work on 10.3+ (maybe even back to 10.1, however I've only compiled for 10.3+)

edit:
Of course, as soon as I post this, I find another option,
Code:
stat /dev/console
which if you parse it, you should be able to find the current console user. I'm not inclined to go this route as Apple might change this behavior and I hate parsing output of unix commands. ;)
 
Wow, so helpful. I read that, I don't think you read my question. If you did, perhaps you know how to use "who" to do what I'm trying to do. "Who" doesn't tell you which user is active, just which users are logged in.

Actually, that's exactly the command you should use. Specifically:

who am i

I'm reviving this thread because I needed an answer to the same question and came here.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.