Read Me About PreLoginAgents.txt

Read Me About PreLoginAgents
============================
1.1
 
Pre-login launchd agents are the best solution to a small but important class of problems.  For example, if you're developing assistive technology for OS X and you want to provide assistance at login time, you will probably need a pre-login launchd agent.
 
To a large extent a pre-login launchd agent can operate like a normal Cocoa application.  However, the pre-login environment results in some unique challenges; this sample shows how to meet those challenges.
 
The pre-login launchd agent technology was introduced in OS X 10.5, so the overall approach shown by this sample should work back to that release.  This project, however, was developed for OS X 10.9.
 
IMPORTANT: Pre-login agents are run asynchronously with respect to login.  If you want to display a user interface that's synchronous with respect to login (for example, to put up a window that the user must interact with before they can log in), you should use an authorization plug-in.  For more information, see TN2228 "Running At Login".
 
<https://developer.apple.com/library/mac/technotes/tn2228/_index.html>
 
Packing List
------------
The sample contains the following items:
 
o Read Me About PreLoginAgents.txt -- This file.
 
o PreLoginAgentCocoa -- A directory containing the code itself.
 
o PreLoginAgentCarbon (legacy) -- A Carbon-based pre-login launchd agent.  This code is no longer being updated.
 
Within the "PreLoginAgentCocoa" directory you'll find:
 
o com.example.apple-samplecode.PreLoginAgentCocoa.plist -- The launchd property list file for the agent.
 
o PreLoginAgentCocoa.xcodeproj -- An Xcode project for the agent.
 
o main.m -- The application main.  This file contains interesting code, /not/ just boilerplate.
 
o Info.plist -- A reasonably standard application property list file.
 
o main.xib -- The user interface for the application.
 
o AppDelegate.{h,m} -- The application delegate.
 
o LogManager.{h,m} -- A class to handle logging.
 
o build -- A pre-built binary.
 
Using the Sample
----------------
To install the sample:
 
1. Change into the sample directory.
 
$ cd Desktop/PreLoginAgents
 
2. Copy the agent to "/Library/PrivilegedHelperTools".
 
$ sudo cp -R PreLoginAgentCocoa/build/Debug/PreLoginAgentCocoa.app \
/Library/PrivilegedHelperTools/
 
3. Copy the property list file to "/Library/LaunchAgents".
 
$ sudo cp PreLoginAgentCocoa/com.example.apple-samplecode.PreLoginAgentCocoa.plist \
/Library/LaunchAgents/
 
4. Log out.
 
At the login screen you will see a window displaying the name of the sample.  When you log in, the agent will quit and the window will go away.
 
Finally, PreLoginAgentCocoa has various user defaults to tweak its behaviour.  The easiest way to set these defaults is to add them as arguments in the launchd property list file ("com.example.apple-samplecode.PreLoginAgentCocoa.plist").  For example, to disable the SIGTERM handling, you can change this:
 
<key>ProgramArguments</key>
<array>
    <string>/Library/PrivilegedHelperTools/PreLoginAgentCocoa.app/Contents/MacOS/PreLoginAgentCocoa</string>
</array>
 
to this:
 
<key>ProgramArguments</key>
<array>
    <string>/Library/PrivilegedHelperTools/PreLoginAgentCocoa.app/Contents/MacOS/PreLoginAgentCocoa</string>
    <string>-CleanExit</string>
    <string>NO</string>
</array>
 
Building the Sample
-------------------
The sample was built using Xcode 5.1 on OS X 10.9.2.
 
Security
--------
IMPORTANT: Pre-login launchd agents are privileged programs; you must code carefully to maintain system security.
 
A pre-login launchd agent is run as root.  This makes a certain kind of sense in that the agent executes before any user has logged in.  However, it does present some security issues.  These are exacerbated by the fact that it's likely that your agent will want to use GUI frameworks, and using GUI frameworks from a root process is a serious security concern.
 
There really is no over-arching solution to this problem.  Rather, there are a number of things that you can do to minimise the risk.
 
o Your agent must not display a menu bar.  Rather, your agent should either be background-only program or a UI element.  You can achieve this by setting either the "LSBackgroundOnly" or "LSUIElement" property in your "Info.plist".
 
o You should try to keep your agent's user interface as simple as possible to reduce the likelihood of security problems.  For example, if you display a text field, you have to worry about whether contextual menu items can be used in that field.  OTOH, if you don't display a text field, you don't have to worry.
 
WARNING: OS X's security infrastructure gets around this problem by running its GUI code as a special user, "_securityagent".  It's hard for third party code to replicate this behaviour because there is no mechanism or policy to create specialist users like this on OS X.  It is /vital/, however, that you not run your agent as "_securityagent".  Doing so could potentially compromise system security.
 
Similarly, you should not reuse other specialist users on the system (for example, "nobody" or "_mdnsresponder").
 
Note:
launchd will not honour the "UserName" and "GroupName" properties in the launchd property list file for a pre-login launchd agent <rdar://problem/5337257>.
 
Logging
-------
The sample logs using the Apple System Log <x-man-page://3/asl> API.  Do the following to see the log entries.
 
1. SSH into the machine.
 
2. Enable 'info' level logging with the following command:
 
$ sudo syslog -c 0 -i
Password:********
 
3. Wait and print new log messages use:
 
$ syslog -w
[...]
 
Quit and Relaunch
-----------------
A launchd agent is not quit like a typical application (that is, by way of the 'quit' Apple event).  Rather, when the system wants your agent to quit, it will send it a SIGTERM.  The code shows a simple technique for catching SIGTERM in a run-loop friendly fashion (see the routine InstallHandleSIGTERMFromRunLoop in "main.m").
 
Also, if your agent quits, launchd will, by default, relaunch it.  You can control this behaviour using various properties in your launchd property list file <x-man-page://5/launchd.plist>.
 
Window Visibility
-----------------
Because of its unique execution environment, a pre-login launchd agent must do some special things to ensure that its windows are visible.  Specifically:
 
o To prevent windows accidentally showing up at login time, OS X 10.5 and later require that you specifically bless such a window before it can appear on the screen.  For an NSWindow, you must call -[NSWindow setCanBecomeVisibleWithoutLogin:].
 
o Finally, due to a problem with the relationship between the UI frameworks and the window server <rdar://problem/5136400>, you must take extra-ordinary steps to show your window.  Specifically, calling -[NSWindow orderFront:] is not sufficient; you must call -[NSWindow orderFrontRegardless].
 
Other Gotchas
-------------
Your pre-login launchd agent will be run redundantly when the user switches directly from user A to user B via the fast user switching menu <rdar://problem/5136392>.
 
CGEventTaps do not work by default in the pre-login environment <rdar://problem/5636091>.  If you need a workaround, please get in touch with DTS.
 
<https://developer.apple.com/support/technical/>
 
Credits and Version History
---------------------------
If you find any problems with this sample, please file a bug against it.
 
<http://developer.apple.com/bugreporter/>
 
1.0 (Oct 2007) was the first shipping version.
 
1.1 (Mar 2014) was a major update to use the latest tools and techniques.
 
Share and Enjoy.
 
Apple Developer Technical Support
Core OS/Hardware
 
28 Mar 2014