While you could clear the various LaunchServices databases by deleting files, I prefer to use a more targeted command line tool when possible. My assumption is that the command line tool is canonical and will therefore produce more reliable results.
In Mac OS X Tiger, you could clear the LaunchServices database using the following “lsregister” command:
$ /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LaunchServices.framework/
Versions/Current/Support/lsregister -kill -r -domain system -domain local -domain user
But, in Leopard, the LaunchServices framework’s path has moved; it is now in the CoreServices framework bundle. You can find it with the “find” command:
$ find /System -name lsregister -type f -print 2>/dev/null
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/
Versions/A/Support/lsregister
Once you find it, you can get the usage statement for the “lsregister” command in Leopard, which will at least tell you what options it has:
$ /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/
Versions/A/Support/lsregister
lsregister: [OPTIONS] [ ... ]
[ -apps [,domain]... ]
[ -libs [,domain]... ]
[ -all [,domain]... ]
Paths are searched for applications to register with the Launch Service database.
Valid domains are "system", "local", "network" and "user". Domains can also
be specified using only the first letter.
-kill Reset the Launch Services database before doing anything else
-seed If database isn't seeded, scan default locations for applications and libraries to register
-lint Print information about plist errors while registering bundles
-convert Register apps found in older LS database files
-lazy n Sleep for n seconds before registering/scanning
-r Recursive directory scan, do not recurse into packages or invisible directories
-R Recursive directory scan, descending into packages and invisible directories
-f force-update registration even if mod date is unchanged
-u unregister instead of register
-v Display progress information
-dump Display full database contents after registration
-h Display this help
Note that if you’re having Launch Services-related problems in one user account but not another, you’ll likely want to start with the “user” domain. (It pays to understand the domain structure of Mac OS X, so that you only make changes where you need to.)
There is some important information in the Info.plist files contained in Mac OS X applications. But, some of that information isn’t really standardized — I’m looking at all of you, you crazy version number and vendor attributes (just the ones I'm interested in!). So, given that, how do you find them all to make comparisons between Apple and third-party apps?
Answer: You can use the “find” command to find all of the Info.plist files for applications in your /Applications directory.
$ find /Applications -name Info.plist -ipath /Applications/\*.app/Contents/Info.plist -depth 3 -print 2>/dev/null
Since that intentionally limits the depth traversal, you could modify it slightly to support finding Info.plists in your /Applications/Utilities directory, as well. However, I would say it’s less likely you’ll be dropping third-party applications in the Utilities directory, so you might not care.
There are cases where you may want to have Launchd run a job once, and then have that job removed so it doesn’t run again. This topic came up on the Launch-Dev mailing list not too long ago and some answers were provided. In particular, Quinn the Eskimo said:
“Alternatively, don’t unload the job. Once a job is loaded, it
doesn’t depend on the plist file, so removing the plist file out from
underneath launchd won’t cause you any problems. The job will
continue to exist in launchd (until the system restarts) but it won’t
get run against unless someone specifically invokes it.”
I recently took a trip to Seattle, and reminded myself of a useful practice I’d developed a while ago. When I’m traveling, I collect import URLs for that trip in my browser — URLs for my organization’s travel booking/information system, airlines, hotels, maps, conference information, etc. — and put them in Safari’s Bookmarks Bar.
It’s pretty easy to collect them:

The resulting bookmarks can be rearranged, if necessary, in Safari’s bookmarks editor. (It’s the “open book” icon in the Bookmarks Bar.)
The entire group of pages can be opened all at once by clicking-and-holding on the folder in the Bookmarks Bar. When the menu drops down for the folder, choose the last command: “Open in Tabs.” This opens all of the sites bookmarked in the folder in separate tabs in the Safari window. I find that it’s useful to have them all open while I’m traveling to my destination, since that way I can see them even if I can’t get an Internet connection — free Wi-Fi is not always available.
When I’m done with the trip, I can delete the entire folder or just some of the bookmarks it contains. To delete the folder quickly, right-click on its name in the Bookmarks Bar and choose “Delete” from the contextual menu.
There can be times when you need to troubleshoot Directory Services in Mac OS X during system starts up. Directory Services apparently sits above and below the kernel — depending on what each of those components needs to do — so having debugging capability early on in the boot process can help you hone in on problems.
To enable debug logging at startup, run the following at the command line:
$ touch /Library/Preferences/DirectoryService/.DSLogDebugAtStart
… and then, on the next restart, debug logging will be enabled. This is equivalent to running:
$ sudo killall -USR1 DirectoryService
… but the advantage is is that debug logging begins at startup, rather than whenever you can log in and start it manually.
Remove the file from the path above when you want to disable the logging for the subsequent restart. As a file whose name starts with a dot, you won’t see it listed by default in the Finder. (The Finder hides dot files by default.)
$ rm /Library/Preferences/DirectoryService/.DSLogDebugAtStart
Drat! I’ve learned that the commands module for Python, which I use, is deprecated and removed in Python according to PEP 3108. That means I can no longer safely call commands.getstatusoutput() anymore. I’ve frequently used this call in the past because it seemed the sanest, easiest way to call for a shell utility and get both its output and return status (for success or error).
I’ll have to find some other way to perform the same function — preferably one that will work on Python 2.3 from Mac OS X Tiger, Python 2.5.1 from Leopard, and future Pythons. The stated replacement for a number of similar modules (including popen2, which frankly kind-of frightened me off with its name) is the subprocess module from PEP 324, but I don’t know if that will work for my purposes.
There are also a bunch of Mac-specific modules being removed. I don’t use any of them right now, but that doesn’t mean they wouldn’t have been useful.
This kind of thing is spirit-crushing to me for some reason. I’m especially annoyed that Python has been around for so long and it is still reorganizing the ways it calls shell commands. Just settle on something! It seems hard to take it seriously as a system administration scripting language when things like this happen.
On the other hand, I love so much of the Python Standard Library, which has afforded me a lot for system administration …
Under normal circumstances, the latest Radmind tools that communicate with the server report client status updates in the Radmind server’s system log. These standard messages can include ones like:
May 8 03:14:56 RadmindServerHost radmind[7890]: report radmind-client.example.com 192.168.7.42 - - ktcheck No updates needed
May 15 03:15:25 RadmindServerHost radmind[24531]: report radmind-client.example.com 192.168.7.42 - - ktcheck Updates retrieved
May 15 03:21:48 RadmindServerHost radmind[24534]: report radmind-client.example.com 192.168.7.42 - - lapply Changes applied successfully
May 15 03:31:07 RadmindServerHost radmind[24356]: report radmind-client.example.com 192.168.7.42 CertificateCN - lapply Error, changes made
The Radmind repo, or “report,” tool provides the ability to send arbitrary messages to the Radmind server process. But how are these messages formatted and sent?
$ repo -e "Debug" -h radmindserverhost.example.com -w2 "Test message"
… results in the system log message:
May 15 03:31:56 RadmindServerHost radmind[25236]: report radmind-client.example.com 192.168.7.42 CertificateCN - Debug Test message
Here, we can see that an entry created with repo looks like the standard Radmind log messages above. The client hostname and IP address are reported after the “report” text. The CertificateCN for the client — if the highest authorization level is specified (with the -w2 flag) — is also listed; if not, a dash takes its place. I haven’t seen a case where the second dash is substituted, however.
Finally, where the Radmind command/tool used would normally be, the “event” specified by repo will printed. After that, the message text appears.
The value proposition is that if you’re using Radmind, the repo command can help you send arbitrary messages to the server for logging. As bonus, if you've taken the time and effort to build the certificate infrastructure for Radmind, you can send these messages securely between the clients and the server cloaked in SSL.
If you’re using multiple servers, you may want to combine their logs in one location so that you can get all of the clients’ reports in one location. You may also want or need to retain these reports for more time. In either case, determine what policies you should apply to the syslog or Apple System Logger (ASL, for Mac OS X) configuration for your server systems.
Whether or not you use repo, it’s good to know that the tools do some logging. The logging can be followed to try to determine the status of your clients, or whether they are failing their updates.
Unfortunately, the most common client failures I have seen tend to involve the lapply tool, and the default level of detail I’ve seen reported back to the server does not provide an indication of what problem has been encountered. You see only that there was an error. Still, even though you may not get enough detail to remotely resolve the problem, it’s something for you to go by find problems in the first place.
There are occasionally times in system administration where I feel as if I’m on the bleeding edge. They are not frequent … which probably puts me somewhere much closer to the lunatic fringe. These are also the times when no one else probably cares about the same things — but no matter, I generally have my own pragmatic reasons for them. This is one of those times, and I have waited for this particular feature for a while. I'm happy to have discovered it.
Let’s set up a scenario: you have a Mac OS X Tiger system bound to an Active Directory. All of the thousands of Active Directory users are available on that Tiger system; any one of them could log in to that computer. This isn’t bad if you only allow logins at the login window. However, if you enable any sharing services, the game changes and your exposure widens.
What if you don’t want everyone in your enterprise directory to log in? This gets a little scary to think about. What if you want to limit those who can log in, particularly for services like SSH? How do you let some users in but keep others out — even going to the point of denying access to whole classes of users?
There are a number of things you can do, some at the client and some in the directory. Unfortunately, some kinds of configuration must be done at the client. There isn’t a great way I’m aware of to distinguish between local and network accounts, complicates the situation for the administrator. With Open Directory in Tiger, any user from any directory service was as valid as any other. There will probably be some manual effort — ongoing effort at that — required to restrict access.
Enter Mac OS X Leopard. It now has “computational groups” in its Open Directory client. They are like smart groups in your local node. The memberships of these groups are calculated, rather than static. They are more like query-based groups.
For the scenario above, the two computational groups that provide the most utility are probably “localaccounts” and “netaccounts.” They allow you to distinguish users who are in the DSLocal database from those in Active Directory. This magic gives the glue we need to maintain finer-grained access controls automatically over time; you could use these groups for the sshd server configuration or in the authorization database at /private/etc/authorization, as examples.
Let’s try the groups out with dsmemberutil, with which we can determine whether or not a user is a member of a specific group. What about the first local user, typically created by the Mac OS X Setup Assistant?
$ dsmemberutil checkmembership -u 501 -G localaccounts
user is a member of the group
$ dsmemberutil checkmembership -u 501 -G netaccounts
user is not a member of the group
Digging further, all of the computational groups I’ve found had the same comment. We can search for that to find the rest, to find all of the computational groups that ship with a Leopard client.
$ dscl . -search /Groups Comment "Group membership calculated by system" | awk '/user|account/ { print $1 }'
authedusers
consoleusers
interactusers
localaccounts
netaccounts
netusers
From that list, we can determine more information about the groups:
| Short name | Real name | GID | Notes |
|---|---|---|---|
| authedusers | Authenticated Users | 50 | |
| consoleusers | Terminal Server User | 53 | |
| interactusers | Interactive | 51 | |
| netaccounts | Network Accounts | 61 | Accounts from a non-local database |
| localaccounts | Local Accounts | 62 | Accounts residing only in one of the local databases |
| netusers | Network | 52 |
Unfortunately, I couldn’t find any other documentation about the computational groups. If you do find some, I’d love to hear about it.
There are times when you need to determine whether a user is a member of a group or not. Knowing the membership of a group can have an impacts on security or operation of system administration scripts. The dsmemberutil tool in Mac OS X Leopard lets you perform checks like this on the command line or in scripts.
You feed it a group and a user, and the tool tells you whether or not the user is a member of the group. This works with group nesting, thanks to the wonder of the memberd functionality first available in Mac OS X Tiger.
$ dsmemberutil checkmembership -u 501 -G admin
user is a member of the group
$ dsmemberutil checkmembership -u 502 -G admin
user is not a member of the group
Unfortunately, you must parse the output, which hinges on whether “not” appears in the text. The exit code for both commands above is “0,” for success.
It took me a little while to figure out how I could tell what DirectoryService directory nodes are available to a Mac OS X system. I could perform the task interactively, just issuing an “ls” command at the dscl “>” prompt. But for a non-interactive one-liner, I was bedeviled by what I thought was a pretty basic function for the dscl tool.
I wanted to find all of the top-level nodes — because, for example, that could tell me if I really had access to an Active Directory or not.
It turns out that I was missing the use of “localhost” as the datastore parameter, which reliably returns the information I wanted in a parsable form. Rather than “/” (simply doesn’t work) or just a period (only uses the local directory) — or even listing "/Active\ Directory" explicitly — “localhost” specifies all of the directories available to the local host. I was incorrectly assuming “localhost” would tie me to the local directory, as with “localonly,” and I thus avoided trying it until I felt like an utter failure.
$ dscl localhost -list .
This produces the following output on a Leopard-based system which only has local accounts in DSLocal:
$ dscl localhost -list .
BSD
Local
Search
Contact
If your system happens to be bound to a Microsoft Active Directory, you’ll instead see it prepended:
$ dscl localhost -list .
Active Directory
BSD
Local
Search
Contact
Or if you’re on Tiger — again bound to Active Directory — you’ll still see “Bonjour,” “NetInfo,” and “SLP” in the mix:
$ dscl localhost -list .
Active Directory
Bonjour
NetInfo
SLP
Search
Contact