Mac OS X: Running items at login
来源:互联网 发布:长虹电视软件下载 编辑:程序博客网 时间:2024/05/16 18:55
Running items at login
Note: Old article topic, just for reference
posted Dec 19, 2008 3:55 PM by Philip Rinehart [ updated May 17, 2009 10:44 PM by Greg Neagle] Written by Greg Neagle Wednesday, 24 November 2004A common need in a managed OS X environment is to run certain scripts every time someone logs in, or to open certain items (applications, background utilities, folders, documents). Apple has provided a method for each user to specify items to be opened at login, but it is not entirely obvious how to specify certain items to be opened or executed for all users of a given computer. Net result: each item in the login items directory is run, opened, or launched. Last Updated ( Thursday, 07 April 2005 )
Fortunately, there is a simple way to do this.
As it turns out, the loginwindow.plist file, located at ~/Library/Preferences/loginwindow.plist
, works the way you'd wish all preference files worked. This file contains the list of items to open at login. If you copy this file to /Library/Preferences/ and make sure it is readable by everyone (chmod o+r /Library/Preferences/loginwindow.plist
), the items you've specified to open at login will now be opened for every user of that machine. What's even better is that if a user specifies his or her own items to be opened at login (using the "My Account" (Jaguar) or "Accounts" (Panther) preference pane), the items defined in/Library/Preferences/loginwindow.plist
AND the items defined for the specific user at ~/Library/Preferences/loginwindow.plist
will be opened. So you can define items to be opened for every user of a machine without interfering with the ability for a user to define their own items.
This technique can be further refined. I have defined a single item to be opened by every user of the machines I manage. It's an application I call "LoginLauncher". This application looks in a folder I've defined (/Library/FA/LoginItems/)
and opens everything in it. It knows how to run AppleScripts; execute shell scripts, Perl scripts, and other UNIX executables; and open anything else the same way the Finder would. The advantage of this method is that you do not have to keep editing /Library/Preferences/loginwindow.plist
- instead, simply add or remove items from /Library/FA/LoginItems/
to control what is open or executed at start up.
The solution detailed here demonstrates several useful techniques that can be used by Mac OS X administrators in a variety of situations.Implementation
This solution has three parts:/Library/Preferences/loginwindow.plist
file: this tells the OS to auto-launch LoginLauncher.app.Login items directory
This is simply a folder in which you put items to be run/opened at login for every user. Mine is at /Library/FA/LoginItems
. You can put yours anywhere you want.LoginLauncher.app
The LoginLauncher application is actually an executable shell script wrapped into an application bundle. This allows us to specify it as an item to be opened at login, control its visibility in the dock, give it a custom icon, and make it look like a "regular" OS X application.
There are several techniques in use here that you might be able to apply to other situations.
We'll start with the shell script. I've highlighted a few interesting parts in red:#!/bin/sh
echo "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`
for file in "$loginItemsDir"/*
do
if [ -x "$file" -a ! -d "$file" ]; then
# execute it
echo "Executing: $file"
"$file" &
else
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 "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 "Opening: $file"
/usr/bin/open "$file"
fi
fi
fi
done
echo "Completed running login items."Parsing the script
I'll point out some interesting parts of the shell script:
Output that is sent via "echo
" goes to the console log. You can view it using Console.app. This is a handy way to debug - just put in echo
statements and check the Console to see that it is doing what you expect.
The script uses the dirname
command and the special variable $0
to find the Contents directory of its own bundle. This works because $0
contains the path to the executable, which is at LoginLauncher.app/Contents/MacOS/LoginLauncher
The first call to dirname
returns /path/to/LoginLauncher.app/Contents/MacOS
and the second returns /path/to/LoginLauncher.app/Contents
Once we have the path to the Contents directory, we can find our Defaults.plist file, and use the defaults
command to extract the path to the login items directory: loginItemsDir=`/usr/bin/defaults read "$contentsdir/Resources/Defaults" LoginItemsDir`
Once we have the path to the login items directory, we loop through every item in it. If a item is executable, we run it. If it's not, we ask the Finder (via osascript
, which is a command-line interface to AppleScript) what kind of file it is. If it's an AppleScript, we ask the Finder to run it. (We could run the AppleScript directly, but if it asks for user interaction or displays a dialog, it gets messier. It's more reliable and safer to ask the Finder to run the AppleScript, since that replicates the environment you probably used to build and test the AppleScript.)
If the file is not an AppleScript, we pass it to the open
command, which opens files and applications much the same way as if you had double-clicked them.Wrapping the script into a bundle
Many Mac OS X applications are really bundles, which is a special kind of directory. The simplest bundle looks like this:
That is, a directory with a name ending in .app, containing a directory named Contents, containing a directory named MacOS, containing the actual executable file. You can convert any executable shell script into a double-clickable application by wrapping it up into a bundle in this way.MyBundle.app/
MyBundle.app/Contents/
MyBundle.app/Contents/MacOS/
MyBundle.app/Contents/MacOS/MyBundle
Note that in this simplest case, the bundle and the executable must have the exact same name - the executable minus the ".app" extension.Stupid bundle tricks
You can make your bundle more Mac-like and control more aspects of its behavior by adding additional files and directories to your bundle. For LoginLauncher.app, I did not want it to appear in the Dock while it was running. It should do its work silently in the background. To achieve this behavior, you must add a "Info.plist" file to the bundle's Contents directory with the following contents:
The Info.plist file can actually contain a good deal more. Indeed, later we will specify a custom icon in this file. But right now, it contains a single key/value pair: <plist>
<dict>
<key>LSBackgroundOnly</key>
<string>1</string>
</dict>
</plist>LSBackgroundOnly = 1
. This tells Launch Services this is a background-only application that displays no windows, has no menubar, and needs no icon in the Dock.Default preferences
I also wanted the path to the login items directory to be stored in a preferences-style file so others could edit it without needing to edit the script itself. This also demonstrates how to store simple data outside of your executable. In this example, I'm storing only a single key/value pair, but you could store many.
Traditionally, internal data an application needs is stored in the bundle's Contents/Resources directory. I created a "Resources" directory inside the Contents directory, and then created file called "Defaults.plist" inside the Resources directory:
This looks far more complicated than it is. In fact, I just copied and pasted the beginning and end of another .plist file I found in my Preferences directory, and then added these two lines:<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LoginItemsDir</key>
<string>/Library/FA/LoginItems</string>
</dict>
</plist> <key>LoginItemsDir</key>
<string>/Library/FA/LoginItems</string>
This allows us to use the defaults
command to read this file. All sorts of preferences can be stored and read this way. This technique does not allow the end-user to modify defaults, but does provide a way to allow an admin to modify preferences for a script without editing the script itself.
In the script itself, this line reads the value we stored in Defaults.plist: loginItemsDir=`/usr/bin/defaults read "$contentsdir/Resources/Defaults" LoginItemsDir`
It uses the defaults
command to read "$contentsdir/Resources/Defaults"
(note no ".plist" at the end of the name) and return a value for the key "LoginItemsDir"
. Earlier in the script, "$contentsdir"
was assigned the path to the application bundle's Contents directory.Adding an icon
Finally, for that professional appearance, all self-respecting Mac applications need their own icon, though it's certainly not necessary for an application like this. I created a custom icon using Icon Composer, part of Apple's Xcode developer tools, free with Mac OS X. I then copied that .icns file to the bundle's Contents/Resources directory. Finally, we have to tell the bundle to use the .icns file by adding two lines to the Contents/Info.plist file:
Note that the Contents/Resources directory is assumed, and the icon file name in the plist does NOT include the .icns extension.<plist>
<dict>
<key>LSBackgroundOnly</key>
<string>1</string>
<key>CFBundleIconFile</key>
<string>LoginLauncher</string>
</dict>
</plist>Pulling it all together with
So - now we have an application that will open or run every item in a directory of our choosing. But we need to ensure that this application will itself be run at every login. We do this by editing the /Library/Preferences/loginwindow.plist
/Library/Preferences/loginwindow.plist
file, creating it if needed.
Here's mine, with the key parts in red:
Specifically, it tells the system to open "LoginLauncher.app", located at "/Library/FA/". You would need to modify the path to match your deployment.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AutoLaunchedApplicationDictionary</key>
<array>
<dict>
<key>Hide</key>
<true/>
<key>Path</key>
<string>/Library/FA/LoginLauncher.app</string>
</dict>
</array>
<key>BuildVersionStampAsNumber</key>
<integer>12977088</integer>
<key>BuildVersionStampAsString</key>
<string>6G30</string>
<key>SystemVersionStampAsNumber</key>
<integer>167904000</integer>
<key>SystemVersionStampAsString</key>
<string>10.2.3</string>
</dict>
</plist>
Make sure the file is readable by everyone, but not changeable by anyone other than the owner (chmod 755 /Library/Preferences/loginwindow.plist
).
Now, at every login, LoginLauncher.app will run and open or run every file in the directory you specified in LoginLauncher.app/Contents/Resources/Defaults.plist
.
To add or remove items that open or run at login for every user, you simply add or remove items from the login items directory.So now what?
The mechanism is in place - what sorts of things can we do with it?
Here are some examples I use:
Quota checks:
We use NFS-mounted home directories. At login, a script is launched that monitors the disk space being used and warns the user as they approach their quota.
Configuration scripts:
I have scripts that add and remove items from a user's Dock based on what is actually installed on a system, a script that offers to help a user configure their account on first login, one that sets up ColorSync profiles for all users of a given machine, and others.
Third-party apps:
There are several third-party apps that open background applications at login.
Some installers set this up for the user that was logged in when the app was installed, but other users do not get these apps auto-launched.
Or the installer does correctly modify the /Library/Preferences/loginwindow.plist
file to auto-launch the helper apps for all users.
If different apps make different modifications to this file, it becomes very difficult to manage with tools like radmind. Therefore, I identify these background "helper" apps and add symlinks to the login items directory that point to these helper apps.
This way, there is a single mechanism that manages these, and I can examine one place (the login items directory) to see what is auto-launched. Some examples:
There are many more.Where to go for more information
Apple Developer bundle documentation:
http://developer.apple.com/documentation/MacOSX/Conceptual/SystemOverview/Bundles/chapter_4_section_1.html
Property lists (.plist files):
http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/Concepts/XMLPListsConcept.html
plist keys for the bundle's Info.plist:
http://developer.apple.com/documentation/MacOSX/Conceptual/BPRuntimeConfig/Concepts/PListKeys.htmldirname, defaults, osascript, open
commands:
Type "man [command name]" at a Terminal prompt.Conclusion
This article details a solution to a specific problem facing Mac OS X administrators: running/opening specific items at logon for all users of a machine.
As part of this solution, several useful techniques for Mac OS X system administrators have been presented:
I hope you find some of these useful for your own environment.
__________
Greg Neagle
- Mac OS X: Running items at login
- MacOsx Running items at login
- Adding Login Items (OS X)
- Mac OS X: Hacking Mac Login Window
- Mac OS X: Enable/Disable/Login Root user and Others
- Mac OS X: 网络管理Login Script来隐藏程序
- Mac OS X: login之前/之后启动程序的方法
- Mac:变更OS X 10.7.4的Login窗口背景
- COCOSDX2.2如何在Mac OS X 中运行Lua (Running Lua on Mac OS X)
- 如何在Mac OS X 中运行Lua (Running Lua on Mac OS X)
- 如何在Mac OS X 中运行Lua (Running Lua on Mac OS X)
- Mac OS X:两种方法改变Login窗口的语言变更/设置
- Enable Remote Login to Start SSH Server in Mac OS X
- PHP case sensitivity based on PHP 5.3 running on Mac OS X
- Mac OS: XAMPP localhost cannot login
- OS X变更Login屏幕背景
- Mac OS X - Miscellaneous
- Securing Mac OS X
- xdebug代码覆盖率原理
- 观影有感
- 【软件测试】操作系统篇:第四章 存储管理
- Mac OS X: Launchd in Depth
- C语言 变量前面有个"&"
- Mac OS X: Running items at login
- rett
- 去年公司带的新人都跳槽了
- C语言 a+=4 和 a++
- Mac OS X:Configuring Access to an Active Directory Domain
- 云计算技术的产生、概念、原理、应用和前景
- cocos2d调用虚拟键盘
- 感谢。。。
- firephp 服务器端使用方式