Monday, November 27, 2006

Running Programs When a User Logs In

OS X provides a graphical tool (System Preferences --> Accounts --> Login Items) that users can use to cause selected applications to launch whenever the user logs in at the console. These settings are saved in the user's home directory, in the file ~/Library/Preferences/loginwindow.plist .

But what about system-wide settings that cause applications to launch for any user? It turns out that a user's loginwindow.plist file can just be copied into /Library/Preferences, and it will affect all users. If a user also has his or her own loginwindow.plist file, all applications in both the system-wide plist file and the user's plist file will be launched when the user logs in at the console.

Instead of editing the system-wide plist file, though, I've adopted the solution described by Greg Neagle in this Macenterprise.org article. Neagle creates a generic loginwindow.plist file that launches one application, called "LoginLauncher.app". This application then looks for files in a specified directory (these can be application bundles, executables, or AppleScripts) and launches each of them. Adding new items to be launched at login only requires dropping the application into the specified directory. This is similar to the MS Windows "Startup" folder.

LoginLauncher.app is an application bundle whose heart is a script called LoginLauncher, which looks like this:

#!/bin/sh

echo "LoginLauncher: Running login items..."

# find the bundle contents dir
macosdir=`/usr/bin/dirname $0`
contentsdir=`/usr/bin/dirname $macosdir`

# use the defaults command to read in the LoginItemsDir from
# $contentsdir/Resources/Defaults.plist
loginItemsDir=`/usr/bin/defaults read "$contentsdir/Resources/Defaults" LoginItemsDir`
echo "LoginLauncher: loginItemsDir is $loginItemsDir"

for file in "$loginItemsDir"/*
do
echo "LoginLauncher: Processing $file..."
if [ -x "$file" -a ! -d "$file" ]; then
# execute it
echo "LoginLauncher: Executing: $file"
"$file" &
else
echo "LoginLauncher: Not an executable file..."
macName=`osascript -e "get POSIX file \"$file\" as text"`
if [ $? -eq 0 ]; then
kind=`/usr/bin/osascript -e "tell application \"Finder\" to get kind of item \"$macName\""`
if [ "$kind" == "Alias" ]; then
kind=`/usr/bin/osascript -e "tell application \"Finder\" to get kind of original item of item \"$macName\""`
fi
if [ "$kind" == "Script" -o "$kind" == "script text" -o "$kind" == "compiled script" ]; then
# run the Applescript
echo "LoginLauncher: Running Applescript: $file"
/usr/bin/osascript -e "tell application \"Finder\" to run script file \"$macName\""
else
# just pass it to the open command, which will launch apps and open docs
echo "LoginLauncher: Opening: $file"
/usr/bin/open "$file"
fi
fi
fi
done

echo "LoginLauncher: Completed running login items."
(This is a slightly modified version of Greg Neagle's script.)

The script first gathers a list of the contents of the login items directory (specified
in a plist configuration file in the LoginLauncher application bundle). For each item in the list, the script then checks to see if the item is an executable file and, if so, executes it. If the item isn't an executable file, the script uses osascript to execute a snippet of code that determines the file's type, then does something appropriate based on that determination.

The AppleScript statement "get POSIX file \"$file\" as text" uses something from the AppleScript "Standard Additions", but I can't find any comprehensive documentation for the Standard Additions online.

No comments: