A year of change and a change of year

It has now been one year since I started my new position with Tamman Technologies and moved my family to the Greater Philadelphia area. It feels like everything has changed, but everything has stayed the same. Plus ça change, plus c’est la même chose, as the saying goes.

It has been a good change, a happy change. While it was a big move, it felt more dramatic to me a year ago than it does now, when it is just reality. I don’t have all of my thoughts about it collected and organized right now, but I felt that I should at least mark this point in time.

I’d also like to thank all of our family and friends that made the move possible, bearable, and successful.

Repo switcheroo

I am a little late to this, but my AutoPkg recipe repository is now available alongside many others. So, I made the switch:

$ autopkg repo-delete “$HOME/Library/AutoPkg/RecipeRepos/com.github.Jaharmi.autopkg_recipes"
$ autopkg repo-add ‘https://github.com/autopkg/jaharmi-recipes.git’

So, if you’ve been following my whimsical AutoPkg repository (Acorn! Fantastical! LaunchBar! XRG!), I suggest you switch, too.

Glob without further comment

>>> import glob
>>> glob.glob(‘/Applications/Install*OS X*.app’)
[‘/Applications/Install Mac OS X Lion.app’, ‘/Applications/Install OS X Mavericks.app’, ‘/Applications/Install OS X Mountain Lion.app’]

Source code update for Penn State MacAdmins Conference 2013 Luggage talk

In case you were still interested, I’ve updated the source code link for my Penn State MacAdmins Conference 2013 Luggage talk. My examples from the talk are finally on-line, so you can follow along in a little more detail if the information already presented in the slides wasn’t enough.

The main repository for the talk contains links to several Mercurial subrepositories, since I tracked each example separately as its own project.

Find the OS X Update in the Munki InstallResults with Python

Munki writes out data into the ManagedInstallReport.plist file when it runs. The InstallResults key in the report shows if anything at all was installed. If something was installed during the last Munki cycle, can we can programmatically filter out just the OS X Update?

The answer is yes! If it weren’t, of course, this would be an even shorter article. No short articles!

Let’s take a look at the Python code to do this. You can enter the following lines of code in the interactive Python interpreter. To get there, type “python” at the command prompt in Terminal on an OS X system.

We’ll start with the assumption that the OS X 10.8.4 update has just been installed. In that case, it would be listed in the current ManagedInstallReport.plist. (This won’t be the case on your own computer, of course, unless the same OS X update was just installed by Munki. For more on other conditions, stay tuned.) Create a variable for the plist file path as follows.

install_report_path = ‘/Library/Managed Installs/ManagedInstallReport.plist’

Import the Python “sys” module and add “/usr/local/munki” to the sys.path. This tells Python to look for importable modules there. This is roughly akin to adding to the PATH environment variable in a UNIX shell.

Since the Munki tools are installed — otherwise, we wouldn’t be worried about interpreting Munki install results — we can depend upon the availability of the “munkilib” Python module. That happens to include FoundationPlist, which is a handy way to read property lists. (In the following example, I import FoundationPlist as “plistlib” to hearken back to the name of an older Python module that did the same.)

import sys
sys.path.append("/usr/local/munki")
from munkilib import FoundationPlist as plistlib

Read the Managed Installs Report plist file from the path given earlier. Pull out only the contents of the “InstallResults” array from the property list data.

report_plist = plistlib.readPlist(install_report_path)
install_results = report_plist[‘InstallResults’]

Iterate through the “InstallResults” array to find each dictionary whose name is “OS X Update” and whose “applesus” value is “true.” There should only ever be one result, because only one OS X update should be installed during any given Munki run.

osx_update = [item for item in install_results if \
    (item[‘name’] == OS X Update’ and \
    item[‘applesus’])]
print(osx_update)

Printing the result displays information from the matching dictionary.

[{
    applesus = 1;
    name = "OS X Update";
    productKey = "041-9635";
    status = 0;
    time = "2013-06-13 01:36:38 +0000";
    version = "10.8.4";
}]

Add your own periodic scripts, subfolders, and launchd tasks

I was involved in a discussion today about the utility of adding scripts that can be run via “periodic.” It’s extremely handy to be able to drop or deploy scripts into the locations examined by periodic when it is scheduled. It’s so handy, I can’t believe I haven’t written about this before.

I’ve long been a fan of /usr/sbin/periodic and its ability to run other scripts. It has been tied together with first cron and then launchd on the OS X platform for years, where maintenance scripts are run on a daily, weekly, and monthly basis.

Periodic itself is a shell script from FreeBSD that runs other executables in a specified directory. It will run any executables at the path specified by the argument following the command. By default, it will treat the argument as a directory within /private/etc/periodic, but you could also specified a arbitrary path.

Apple’s OS X maintenance jobs consist of scripts found within periodic subfolders:

% ls -l /private/etc/periodic
total 0
drwxr-xr-x  11 root  wheel  374 Jun 20  2012 daily
drwxr-xr-x   5 root  wheel  170 Jun 20  2012 monthly
drwxr-xr-x   4 root  wheel  136 Jun 20  2012 weekly

Running “periodic daily” to execute the contents of the “daily” folder above is triggered by /System/Library/LaunchDaemons/com.apple.periodic-daily.plist.

<?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>Label</key>
        <string>com.apple.periodic-daily</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/sbin/periodic</string>
                <string>daily</string>
        </array>
        <key>LowPriorityIO</key>
        <true/>
        <key>Nice</key>
        <integer>1</integer>
        <key>StartCalendarInterval</key>
        <dict>
                <key>Hour</key>
                <integer>3</integer>
                <key>Minute</key>
                <integer>15</integer>
        </dict>
        <key>AbandonProcessGroup</key>
        <true/>
</dict>
</plist>

The weekly and monthly launchd jobs are similar, differing in their StartCalendarInterval values. I’m pretty sure that the timing of the weekly job has shifted from previous OS X releases to what I see on my system today.

Job Day Time (system local)
Daily Every day 3:15 AM
Weekly Every week, day 6 (Saturday) 3:15 AM
Monthly Every month, day 1 5:30 AM

Once you place a new script into any of the existing folders, it will be called on the schedule specified by these three launchd tasks. The next time it runs, the periodic utility automatically picks up on any new executables in whatever subfolder it examines.

But, you don’t have to stop there. You can add your own periodic subfolders and wire them up with launchd. Add your own subfolders in /private/etc/periodic. Then, these subfolders can be activated via new launchd jobs that you create (and place in /Library/LaunchDaemons).

For example, to run all of the scripts in the new folder /private/etc/periodic/morning before the start of each business day, you could specify:

<?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>Label</key>
        <string>org.bitbucket.periodic-morning</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/sbin/periodic</string>
                <string>morning</string>
        </array>
        <key>LowPriorityIO</key>
        <true/>
        <key>Nice</key>
        <integer>1</integer>
        <key>StartCalendarInterval</key>
        <dict>
                <key>Hour</key>
                <integer>6</integer>
                <key>Minute</key>
                <integer>30</integer>
        </dict>
        <key>AbandonProcessGroup</key>
        <true/>
</dict>
</plist>

Munki archives ManagedInstallReport.plist files

In researching the InstallResults key from the ManagedInstallReport.plist, I found that Munki automatically archives previous ManagedInstallReport.plist files. The archive is stored at the path /Library/Managed Installs/Archives.

I had 100 timestamped, archived reports there. That was such a specific number that it didn’t seem to be a coincidence. Sure enough, in the source code of munkicommon.py, there is a segment devoted to trimming the archived reports down to the last 100. Excess reports over 100 are deleted.

These archived reports are helpful if you are seeking to find am example of something specific in the ManagedInstallReport output. In my case, I was looking to see how the InstallReport presented a system software installation, like the OS X 10.8.4 Update. On my home systems, I install those packages through the standard public Apple Software Update mechanism, but the update itself could have been obtained through a Software Update Server (SUS).

Luckily, the installation of the OS X 10.8.4 Update was recent enough that it did appear in the AppleUpdates and InstallResults of some of my archived ManagedInstallReport files. To find where, I opened up the “Archives” folder as a Project in BBEdit and performed a Project-wide search for “OS X Update.” When I found a single file with two “hits,” I was reasonably sure that was the one where the install happened. For reference, its AppleUpdates key:

<key>AppleUpdates</key>
<array>
    <dict>
        <key>RestartAction</key>
        <string>RequireRestart</string>
        <key>description</key>
        <string></string>
        <key>display_name</key>
        <string>OS X Update</string>
        <key>installed_size</key>
        <integer>148167</integer>
        <key>name</key>
        <string>OSXUpd10.8.4</string>
        <key>productKey</key>
        <string>041-9635</string>
        <key>version_to_install</key>
        <string>10.8.4</string>
    </dict>
</array>

… and the InstallResults key:

<key>InstallResults</key>
<array>
    <dict>
        <key>applesus</key>
        <true/>
        <key>name</key>
        <string>OS X Update</string>
        <key>productKey</key>
        <string>041-9635</string>
        <key>status</key>
        <integer>0</integer>
        <key>time</key>
        <date>2013-06-13T01:36:38Z</date>
        <key>version</key>
        <string>10.8.4</string>
    </dict>
</array>

InstallResults key in the Munki ManagedInstallReport.plist

Munki keeps track of various aspects of its managed software update process in the “/Library/Managed Installs/ManagedInstallReport.plist” file. I can’t take any credit for any clever uses of “ManagedInstallReport.plist,” but I did want to jot down a few things I learned about it.

Before and during a Managed Software Update run, the “InstallResults” key maps to an empty array.

<key>InstallResults</key>
<array/>

Immediately afterwards, the array will expand to contain dictionaries representing each item that was installed. The array contents remain until the next Munki run.

In the following example, BBEdit v10.5.4 was installed by Managed Software Update.

<key>InstallResults</key>
<array>
        <dict>
                <key>applesus</key>
                <false/>
                <key>download_kbytes_per_sec</key>
                <integer>0</integer>
                <key>duration_seconds</key>
                <integer>2</integer>
                <key>name</key>
                <string>BBEdit</string>
                <key>status</key>
                <integer>0</integer>
                <key>time</key>
                <date>2013-06-26T01:10:30Z</date>
                <key>version</key>
                <string>10.5.4</string>
        </dict>
</array>

Parsing the “InstallResults” array after a Munki run can be useful. Here are some points to consider:

  • A non-empty array means that something was installed in the last Managed Software Update run. Therefore, if you detect this condition, you can assume some change — in the form of one or more installations — has taken place. More on this in a future post.
  • The contents of the populated “InstallResults” array can be further parsed. You can detect whether some software was installed by the values of any of the dictionary keys — the “name,” the “version,” or even the “applesus” boolean. Finding something particular in the fields in this array could lead to a specific follow-up action.

Demo mirrored text document windows with BBEdit

As mentioned in the previous installments in this small series, Demo mirrored Terminal sessions with screen and Demo mirrored Finder windows, I included a live demonstration element in my Luggage talk at the Penn State MacAdmins Conference 2013.

The live demonstration involved applications that allowed me to focus my attention on the presenter’s screen in front of me while simultaneously projecting the same information to the audience — all while using video spanning instead of video mirroring.

I used this third specific technique to demo real-time editing of a text file on my secondary display in BBEdit 10.5 from Bare Bones Software. Of the three techniques, this is the one that flopped in actual execution. However, I believe that it would have worked with more practice.

First, I opened the text document in a BBEdit window. This first window was the one I watched on my laptop’s primary display.

DualScreenPresentationWithBBEdit01-80.png

I left the font and other display settings on this first window set at their defaults, as specified in the BBEdit preferences. Since I was going to be viewing this on my screen, my normal settings worked just fine.

Second, I made sure that the first BBEdit window was showing the Files sidebar with View > Show Files command. (While this is not technically necessary, I like it this way.)

Third, I right-clicked on the name of the text file in the Files sidebar of the first BBEdit window. From the contextual menu, I chose the first command, “Open in Additional Window.” This opened a second window with the same content. All of the windows displaying that text file’s content are updated as changes are made to the file. You could also use View > Open in Additional Window with or without the Files sidebar open.

DualScreenPresentationWithBBEdit03-80.png

Fourth, while in the second BBEdit window, I selected Window > Synchro Scrolling command. This changes BBEdit’s default scrolling behavior. With synchro scrolling, vertical scrolling actions in any of the related windows then trigger scrolling in the others. This is not strictly necessary — if you have enough textual content to demand scrolling, it may simply be too much for a live demonstration — but I enabled it for good measure.

Fifth, I greatly increased the font size to scale the text up for the audience viewing the secondary display. This took me a while — along with a support request to the ever-patient Bare Bones Software — to figure out, because I wanted different display settings for each window. For a day or so, I despaired that I’d ever be able to do it.

DualScreenPresentationWithBBEdit04-60.png

The answer was simple, and I found it as I was waiting for the quick answer from Bare Bones support. While the second window was active, I pressed Command-T to bring up the standard system Type panel (which is labelled “Fonts”). You can also access it from View > Text Display > Show Fonts. From the Fonts panel, I changed the type size to 36 points for the whole window and only that window.

DualScreenPresentationWithBBEdit05-80.png

Once all of this is set up, BBEdit handles all the work to make the second window act as a real-time duplicate of the first window.

At the conference, I arranged everything before the presentation began. Somewhere in that preparation, I believe I messed up, which led to my problems making this work as intended. However, in my defense, the BBEdit window arrangement feel more complicated. (For that reason, I’d either like to find a script to do this for me, or cobble one together.) At the appropriate moment when I dropped out of Keynote, I was ready for the two-window demonstration. When the demo was done, I switched to Keynote to continue with slides.

My earlier advice about using this technique sparingly applies here, as well. Perhaps doubly so, since the setup in BBEdit has a few more steps.

Demo mirrored Finder windows

As mentioned in Demo mirrored Terminal sessions with screen, I wanted to include a live demonstration element in my Luggage talk at the Penn State MacAdmins Conference 2013. I used a second specific technique to demo Finder windows screens in real-time on my secondary display. As with the Terminal portion of the demonstration, this mirroring enabled me to concentrate primary display in front of me.

To create this effect, I set up two Finder windows. Remember … the OS X Finder has always had the somewhat controversial capability of displaying the same content in two different windows. (Whether I like or dislike this capability, I’m willing to take advantage of what it offers in this case.) Each window displaying that content is updated when the underlying files and folders change.

The first window was the one I watched on my laptop’s primary display. It was set up with my standard Finder window settings, although I found it useful to switch to column view when dragging files into and out of subfolders.

DualScreenPresentationWithFinder01-80.png

The second window mirrored the first for demonstration purposes. It was moved to the secondary display, where my slides would normally appear, as part of my preparation for the talk. I selected specific View Options for this Finder window.

DualScreenPresentationWithFinder02-80.png

The first goal was to make the window as readable as possible by the audience when projected. The second goal was to focus on the version number of applications, so I enabled that normally-hidden column in the Finder list view. I set these View Options while practicing with a second display and then dragged it into position on the projection screen for the talk.

DualScreenPresentationWithFinder03-80.png

The Finder handles the heavy lifting so that the second window becomes a real-time duplicate of the first window.

At the conference, I arranged everything before the presentation began. Then, at the appropriate moment, I dropped out of Keynote and switched to the Finder for the demonstration. When the demo was done, I switched to Keynote to continue with slides.

My earlier advice about using this technique sparingly applies here, as well.

Mac developers

Syndicate content