TweetFollow Us on Twitter

MacEnterprise: System Framework Scripting

Volume Number: 25
Issue Number: 08
Column Tag: MacEnterprise

MacEnterprise: System Framework Scripting

Using system frameworks in scripts for systems administration

By Greg Neagle, MacEnterprise.org

Introduction

A very important tool in the systems administrator's tool kit is scripting. It's often been said that a good systems administrator is a lazy systems administrator. A good sysadmin will try to minimize the number of repetitive tasks he or she has to perform by automating them. What good are all our fancy computers if we cannot get them to do our boring work for us? So it is very common for a systems administrator to use scripting to automate a repetitive, complex and/or error-prone task.

Several common systems administration problems can often be solved through the use of creative scripting. You could have a script that runs system_profiler on all your Macs and uploads information about all your machines to a central database. Scripts can help with the initial setup of a machine, or initial application configuration. Scripts can monitor for problems and report them to you.

Another typical use for scripting is to fill in missing functionality. An example: Apple's Energy Saver can sleep an idle machine, or shut down and wake up a machine on a schedule. But what if you wanted Energy Saver to leave a machine alone between 8:00 am and 6:00 pm, but outside of those hours, you'd like it to sleep idle machines or even shut them down. Energy Saver's preferences don't offer this functionality, but you can easily script this using either the pmset or systemsetup command-line tools.

Scripting Languages

There are many scripting languages available in a default install of OS X 10.5. Among them are AppleScript, the traditional shell languages – sh, csh, tsch, zsh and bash; Perl, PHP, Python, and Ruby. So which do you use? The answer, of course, is "it depends."

The shell languages are among the easiest to get started in for simple tasks, as you can often just list the commands you want to perform, just as if you'd typed them at the command line. Here's an example of a simple shell script that configures time and date settings:

#!/bin/sh
/usr/bin/systemsetup –setnetworktimeserver time.myorg.org
/usr/bin/systemsetup –setusingnetworktimeon
/usr/bin/systemsetup –settimezone "America/Los_Angeles"

This script only calls one tool – systemsetup – to do its work, but it is common to call several command-line tools in scripts to complete a given task. Let's say we want to configure Energy Saver settings, but only on desktop machines – we'll leave laptops alone. So we need a way to tell if the script is running on a laptop, and we need a way to set Energy Saver settings. system_profiler can tell us the machine model, and pmset can set Energy Saver settings, so it makes sense to use those tools for a script:

#!/bin/sh # check to see if we're a laptop IS_LAPTOP=`/usr/sbin/system_profiler SPHardwareDataType | grep "Model" | grep "Book"` if [ "$IS_LAPTOP" = "" ]; then # sleep never, disk sleep in 10 minutes, # display sleep in 30 minutes pmset -c sleep 0 disksleep 10 womp 1 displaysleep 30 fi

In this example, we call use grep to filter the output from system_profiler, looking for "Book" in the Model Name. Here's how those steps look from the command line:

> system_profiler SPHardwareDataType | grep "Model" | grep "Book"
      Model Name: MacBook Pro
      Model Identifier: MacBookPro5,1

This works because all Apple laptops to date have "Book" in their names (PowerBook, iBook, MacBook, MacBook Pro, MacBook Air). If we find "Book", then the machine we're running on is a laptop. If we don't find "Book", we use the pmset command to set the power management options we want for desktop machines. This shell script uses three command-line tools (system_profiler, grep, and pmset) to do its thing.

You can certainly do more complex things in shell languages, but it's difficult to work with complex data structures like arrays and dictionaries, and there's no support for object-oriented programming. For simple tasks like the above, it may not be worth the effort of writing the script in anything other than shell. But once your script reaches a certain level of complexity, you should consider using a higher-level scripting language like Perl, Python, or Ruby.

Higher-level scripting languages

Two higher-level languages commonly used for systems administration tasks are Perl and Python. Perl has a large number of available libraries, and does text manipulation really well. This shouldn't be surprising, since Perl was originally written to make report processing easier.

In recent years, Python has been gaining popularity as a systems administration language. There are some key features that make Python attractive for this task. First, a core design goal for Python is to maximize its readability. Python programs tend to be easier to read, and therefore easier for others to understand and maintain. This is no small feature in an organization where a systems administrator may be called to fix or extend a script written by someone else. Another feature adding to Python's suitability for systems administration tasks is its large and useful standard library.

With the 10.5 release of OS X, there is another reason to consider Python for systems administration tasks: easy access to system frameworks.

Why Frameworks?

As a systems administrator, if you need to script a task, typically one of the first things you do is look for command-line tools to do some or most of the work. In the earlier shell scripting examples, we used the command-line tools systemsetup, system_profiler, grep, and pmset. There are many, many other command-line tools of use to a systems administration scripter.

But what if the functionality you need is not available in a command-line tool? There are many things you can do in OS X that are not available via the command-line. If you are scripting in shell and need access to functionality not exposed via a command-line tool, you might be out of luck. But if you are using Python or Ruby, Apple has included "bridges" to some of the system frameworks, allowing you to call native OS X methods from inside your Python or Ruby script.

Framework Example

Let's look at an example where access to a system framework can help solve a systems administration problem.

OS X systems administrators are familiar with OS X's standard method of storing and retrieving preferences. Sometimes referred to as the "defaults" system, preferences are stored in plist files located in /Library/Preferences, ~/Library/Preferences, and ~/Library/Preferences/ByHost. Additionally, some preferences can be managed via MCX. A problem is determining the "effective" preferences – that is, what preferences are actually in effect for the current user on the current machine.

Apple provides some command-line tools: defaults can read the user's on-disk preferences and return them, but it isn't MCX aware, so managed preferences are not found by the defaults tool. The mcxquery tool can list all of the managed preferences in effect for a given user and/or computer, but it's up to you to parse that information to find the preference domain and key you are interested in. There is no command-line tool that allows you to ask for the value of a specific preference that returns the effective value taking into consideration MCX settings.

Since Python can access OS X system frameworks, and since the information we need can be obtained by calling some functions in the CoreFoundation framework (namely the CFPreferences functions), we can write a tool in Python to give us the information we want. Here's the basic idea:

#!/usr/bin/env python
import sys
import CoreFoundation
preferenceDomain = sys.argv[1]
keyName = sys.argv[2]
print CoreFoundation.CFPreferencesCopyAppValue(keyName, preferenceDomain)
if CoreFoundation.CFPreferencesAppValueIsForced(keyName, preferenceDomain):
    print "*** %s is managed always by MCX ***" % keyName
The main bit of magic is the line:
import CoreFoundation

which imports the CoreFoundation framework, where the CFPreferences functions are defined. Once we import this framework, we can call the CFPreferences functions just as if they were defined in a Python library.

CoreFoundation.CFPreferencesCopyAppValue(keyName, preferenceDomain) gives us the value defined for the key keyName in the preferences domain preferenceDomain, no matter where this is defined – in ByHost preferences, user preferences, system-wide preferences, or managed preferences (those managed by MCX).

CoreFoundation.CFPreferencesAppValueIsForced(keyName, preferenceDomain) can tell us if this value is being "forced" by MCX – that is, the value is set to be managed "always".

Let's look at it in action. I've named this script "effective_defaults". First, let's read a preferences setting using the built-in defaults command:

> defaults -currentHost read com.apple.screensaver askForPassword
2009-06-27 18:28:09.312 defaults[58288:807] 
The domain/default pair of (com.apple.screensaver, askForPassword) does not exist
The defaults command would lead us to believe that the screensaver will not ask us for a password, yet on my laptop, it does. Let's see what our effective_defaults script says:
> ./effective_defaults com.apple.screensaver askForPassword
1
*** askForPassword is managed always by MCX ***

Since this script uses CFPreferences, it is MCX-aware, and returns "1" for the setting, and tells us MCX is managing this value "always".

Another example – on my machine, the loginwindow displays username and password fields, not a list of users. Why is that? Let's ask defaults:

> defaults read /Library/Preferences/com.apple.loginwindow SHOWFULLNAME
2009-06-27 18:53:49.470 defaults[58353:807] 
The domain/default pair of com.apple.loginwindow, SHOWFULLNAME) does not exist

This tells us that the SHOWFULLNAME preference is not set in /Library/Preferences/com.apple.loginwindow.plist. Now, let's ask using the Python script:

> ./effective_defaults com.apple.loginwindow SHOWFULLNAME
True
*** SHOWFULLNAME is managed always by MCX ***

Again, it finds the MCX-managed value and reports it. Let's check to make sure it does the right thing when the value is not managed by MCX. I've set the image that appears behind the loginwindow to a custom image. Let's check it both ways:

> defaults read /Library/Preferences/com.apple.loginwindow DesktopPicture
/Library/Desktop Pictures/Disney/Goofy.jpg
> ./effective_defaults com.apple.loginwindow DesktopPicture
/Library/Desktop Pictures/Disney/Goofy.jpg

We see that both methods return the same value. Notice that in the effective_defaults script, we don't have to know that the value is stored in the file in /Library/Preferences, and in fact we cannot specify a file path, only a preferences domain.

Improving the script

Let's return to the script for a bit. It's actually not very well written – if we pass the wrong number of parameters, it fails unhelpfully:

> ./effective_defaults com.apple.loginwindow
Traceback (most recent call last):
  File "./effective_defaults", line 7, in <module>
    keyName = sys.argv[2]
IndexError: list index out of range

This is because we didn't do any kind of error checking or error-handling. Let's fix that:

#!/usr/bin/env python
import sys
import CoreFoundation
try:
    preferenceDomain = sys.argv[1]
    keyName = sys.argv[2]
except:
    print "Usage: %s <domain> <key>" % sys.argv[0]
    print "\tWhere <domain> is a valid preferences (defaults) domain,"
    print "\tand where <key> is a valid preferences key"
    print
    print"Example: %s com.apple.screensaver askForPassword" % sys.argv[0]
    exit(-1)
    
print CoreFoundation.CFPreferencesCopyAppValue(keyName, preferenceDomain)
if CoreFoundation.CFPreferencesAppValueIsForced(keyName, preferenceDomain):
    print "*** %s is managed always by MCX ***" % keyName

All we've done here is wrap the code that gets the parameters with a try/except block. If there's a problem, we print a usage statement and exit. Now let's try it:

> ./effective_defaults com.apple.loginwindow 
Usage: ./effective_defaults <domain> <key>
   Where <domain> is a valid preferences (defaults) domain,
   and where <key> is a valid preferences key
Example: ./effective_defaults com.apple.screensaver askForPassword

There's certainly more that could be done to improve and extend the script, but this gets the basic functionality running and handles the most common error cases.

More Frameworks

Being able to access system frameworks opens up an entirely new realm of tools for systems administrators to use to solve problems. In many ways, systems administrators using Python or Ruby have almost as many options as people coding in lower-level languages like Objective-C, C, or C++. A better developed, and more generally useful example is crankd. crankd is a Python project that began as a replacement for the Kicker.bundle functionality in older versions of OS X. Prior to Leopard, systems administrators could use the SystemConfiguration Kicker.bundle to run scripts when the network configuration changed – the computer connected or disconnected from a network, or the IP address changed, or similar network events. But with the release of OS X 10.5 Leopard, the Kicker.bundle disappeared, and there was no obvious replacement method for systems administrators to run scripts based on network changes. (To be fair, Apple never officially supported the use of the Kicker.bundle in this manner).

Chris Adams and Nigel Kirsten collaborated on what became crankd, which is part of PyMacAdmin, a collection of Python-based utilities of interest to Mac systems administrators. PyMacAdmin uses Python and its ability to call system code to do things that are impossible or difficult from command-line tools.

crankd not only replaces the lost Kicker functionality, but adds much more. With crankd, you can watch for network changes, filesystem activity, application launches, volume mounting/unmounting, system sleep/wake, and more. When any of these events occur, crankd can run a script or call a Python method.

crankd makes use of the Cocoa, SystemConfiguration and FSEvents frameworks. Other PyMacAdmin tools make use of the Security and CoreFoundation frameworks, so if you are looking for more examples of how to work with OS X system frameworks with Python from a systems administration perspective, this is a good place to start.

Check out crankd and the other PyMacAdmin tools at http://code.google.com/p/pymacadm.

Where to Go from Here

You've now seen how you can work with OS frameworks in Python scripts. When scripting, you now have a whole new set of resources you can use to accomplish your task. To find out more about the various frameworks so you can use them in your Python scripts, start with Apple's documentation, both online and included with the Xcode tools.

Calling system frameworks in scripts is not limited to Python. Apple ships the RubyCocoa bridge with Leopard, which enables Ruby scripts to call Objective-C frameworks. And finally, there is CamelBones, a third-party bridge between Perl and Objective-C.

Apple documentation on Python and Ruby on Mac OS X, including info on the Cocoa bridges:

http://developer.apple.com/documentation/Cocoa/Conceptual/RubyPythonCocoa/Articles/RubyPythonMacOSX.html

Apple Cocoa documentation:

http://developer.apple.com/documentation/Cocoa/index.html

Apple documentation on CFPreferences:

http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFPreferences/CFPreferences.html

Official PyObjC site:

http://pyobjc.sourceforge.net/

RubyCocoa site:

http://rubycocoa.sourceforge.net/HomePage

CamelBones, the Perl/Objective-C bridge:

http://camelbones.sourceforge.net/index.html

And in a recent MacTech:

Mac in the Shell: Python on the Mac: PyObjC, Edward Marczak, June 2009

If you have Xcode installed, (and as a MacTech-reader, you should) you'll find PyObjC examples at /Developer/Examples/Python/PyObjC and RubyCocoa examples at /Developer/Examples/Ruby/RubyCocoa.


Greg Neagle is a member of the steering committee of the Mac OS X Enterprise Project (macenterprise.org) and is a senior systems engineer at a large animation studio. Greg has been working with the Mac since 1984, and with OS X since its release. He can be reached at gregneagle@mac.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Navicat Premium Essentials 12.1.25 - Pro...
Navicat Premium Essentials is a compact version of Navicat which provides basic and necessary features you will need to perform simple administration on a database. It supports the latest features... Read more
Sketch 58 - Design app for UX/UI for iOS...
Sketch is an innovative and fresh look at vector drawing. Its intentionally minimalist design is based upon a drawing space of unlimited size and layers, free of palettes, panels, menus, windows, and... Read more
ClipGrab 3.8.5 - Download videos from Yo...
ClipGrab is a free downloader and converter for YouTube, Vimeo, Facebook and many other online video sites. It converts downloaded videos to MPEG4, MP3 or other formats in just one easy step Version... Read more
Dash 4.6.6 - Instant search and offline...
Dash is an API documentation browser and code snippet manager. Dash helps you store snippets of code, as well as instantly search and browse documentation for almost any API you might use (for a full... Read more
FotoMagico 5.6.8 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more
Civilization VI 1.2.4 - Next iteration o...
Sid Meier’s Civilization VI is the next entry in the popular Civilization franchise. Originally created by legendary game designer Sid Meier, Civilization is a strategy game in which you attempt to... Read more
Skype 8.52.0.138 - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
Bookends 13.2.6 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Bookends uses the cloud to sync reference libraries on all the Macs you use.... Read more
BusyContacts 1.4.0 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Chromium 77.0.3865.75 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 77.0.3865.75: A list of changes is available... Read more

Latest Forum Discussions

See All

Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
PSA: GRIS has some issues
You may or may not have seen that Devolver Digital just released GRIS on the App Store, but we wanted to do a quick public service announcement to say that you might not want to hop on buying it just yet. The puzzle platformer has come to small... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »
Sky Patrol (Games)
Sky Patrol 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: 'Strategic Twist On The Classic Shooter Genre' - Indie Game Mag... | Read more »

Price Scanner via MacPrices.net

Preorder your Apple Watch Series 5 today at A...
Amazon has Apple Watch Series 5 GPS models available for preorder and on sale today for $15 off Apple’s MSRP. Shipping is free and starts on September 20th: – 40mm Apple Watch Series 5 GPS: $384.99 $... Read more
21″ iMacs on sale for $100 off Apple’s MSRP,...
B&H Photo has new 21″ Apple iMacs on sale for $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Overnight shipping... Read more
2018 4 and 6-Core Mac minis on sale today for...
Apple resellers are offering new 2018 4-Core and 6-Core Mac minis for $100-$150 off MSRP for a limited time. B&H Photo has the new 2018 4-Core and 6-Core Mac minis on sale for up to $150 off... Read more
Save $150-$250 on 10.2″ WiFi + Cellular iPads...
Verizon is offering $150-$250 discounts on Apple’s new 10.2″ WiFi + Cellular iPad with service. Buy the iPad itself and save $150. Save $250 on the purchase of an iPad along with an iPhone. The fine... Read more
Apple continues to offer 13″ 2.3GHz Dual-Core...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases are new... Read more
Apple restocks 2018 MacBook Airs, Certified R...
Apple has restocked Certified Refurbished 2018 13″ MacBook Airs starting at only $849. Each MacBook features a new outer case, comes with a standard Apple one-year warranty, and is shipped free. The... Read more
Sunday Sale! 2019 27″ 5K 6-Core iMacs for $20...
B&H Photo has the new 2019 27″ 5K 6-Core iMacs on stock today and on sale for up to $250 off Apple’s MSRP. Overnight shipping is free to many locations in the US. These are the same iMacs sold by... Read more
Weekend Sale! 2019 13″ MacBook Airs for $200...
Amazon has new 2019 13″ MacBook Airs on sale for $200 off Apple’s MSRP, with prices starting at $899, each including free shipping. Be sure to select Amazon as the seller during checkout, rather than... Read more
2019 15″ MacBook Pros now on sale for $350-$4...
B&H Photo has Apple’s 2019 15″ 6-Core and 8-Core MacBook Pros on sale today for $350-$400 off MSRP, starting at $2049, with free overnight shipping available to many addresses in the US: – 2019... Read more
Buy one Apple Watch Series 5 at Verizon, get...
Buy one Apple Watch Series 5 at Verizon, and get a second Watch for 50% off. Plus save $10 on your first month of service. The fine print: “Buy Apple Watch, get another up to 50% off on us. Plus $10... Read more

Jobs Board

*Apple* Mobility Pro-Store 149 - Best Buy (U...
**731985BR** **Job Title:** Apple Mobility Pro-Store 149 **Job Category:** Store Associates **Location Number:** 000149-Towson-Store **Job Description:** At Best Read more
Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
Windows/ *Apple* Technical Support Engineer...
Windows/ Apple Technical Support Engineer McLean , VA , US Apply + Be you + Be Booz Allen + Be empowered + Learn More Job Description Location: McLean, VA, US Job Read more
*Apple* Mobile Master - Best Buy (United Sta...
**725617BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 001095-Chesterfield-Store **Job Description:** **What does a Read more
Geek Squad *Apple* Master Consultation Agen...
**732415BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Job Category:** Services/Installation/Repair **Location Number:** 000425-Hickory-Store **Job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.