Mar 17

TerminalAfter graduation and my last day at work, I’ve taken a road trip to visit the Bennett’s in D.C. and was promptly chagrined while trying to show off Leopard’s screen sharing over OpenVPN.

Fortunately, it’s pretty easy to turn on Screen Sharing from an SSH session.

echo -n enabled > /Library/Preferences/com.apple.ScreenSharing.launchd

Launchd should automatically start the Screen Sharing service when this file is modified.

More information is available at Apple Remote Desktop: Configuring remotely via command line (kickstart)

Feb 29

VNC GuestDigging around in a NetBoot-Install.dmg file created by NetRestore Helper, I found a nice little gem.

In Leopard, and perhaps earlier versions of Mac OS X, we’re able to start a VNC server with the machine serial number as a password. This is particularly interesting for a managed network or lab environment.

As an example, I’m starting a VNC server in my NetBoot-Install image with the following shell script:

# Credit to Mike Bombich for this snippet

VNC="/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/MacOS/AppleVNCServer"

if [ -x "$VNC" ]; then
    "$VNC" -noRegister -serialNumber &
fi

I’m then able to quickly connect with Cmd+K in the finder:
Connect to Server

If you’re scripting this, here’s a quick way to snag the serial number. I do this before I bless a client machine to netboot, so I have the serial number to connect back up once it’s in the NetRestore system.

system_profiler SPHardwareDataType | \
  grep -i 'serial number' | \
  perl -ple 's/.*:\s+(\w+).*?/$1/'
Feb 17

After upgrading all of my personal machines to Leopard, I’ve found myself using the Screen Sharing feature quite often. Many people have two Mac’s these days, particularly owners of the MacBook Air, and screen sharing makes it incredibly convenient to access a machine in another room.

As with most things I do frequently, Quicksilver has utterly spoiled me. The process of making the Finder active, pressing Command+K, and selecting or typing vnc://champ.local is just way too long.

Fortunately, it’s really easy to integrate Screen Sharing into our Quicksilver workflow.

Here’s how.

You’ll need to edit unix plaintext files, rather than rich text which TextEdit.app seems to insist on producing. TextWrangler is a great, free, text editor for editing Unix plain text files, although I’m partial to TextMate.

First, make sure Screen Sharing is turned on in the Sharing Preference Pane in Leopard.

Sharing Pref Pane

Suppose you want to connect to a machine named “champ” in the Sharing Preference Pane.

The script will have the contents:

#!/bin/sh
# Nice and short
open vnc://champ.local &

Save the script into ~/Library/Application Support/Quicksilver/Scripts/champ.sh and make sure that folder is scanned by Quicksilver.

You’ll also need to make sure the script is executable, so open up Terminal.app and change the permissions:

chmod a+x ~/"Library/Application Support/Quicksilver/Scripts/"*.sh

That’s it. Now you should just be able to invoke Quicksilver, start typing the name of the machine you want to share the screen with, and presto! Nice and fast.

Quicksilver VNC

Feb 07

TerminalI’ve been using the find command for over a decade now, and I’m ashamed to say I never really learned how to properly exclude directories. Dealing with with subversion working copies that litter “.svn” folders everywhere, I finally sorted it all out this afternoon.

To exclude “.svn” folders and all contents:

$ find . '!' '(' -name '.svn' -prune ')'

This, combined with find -print0 and xargs -0 to execute arbitrary commands on every filesystem object found is a wonderful tool to keep handy.

Feb 07

PuppetConsider the following statement in a puppet manifest (think of a manifest as a script).

node "subversion.math.ohio-state.edu" {
    subversion::server::webrepository {
        "support": path => "/var/svn/support";
        "test":    path => "/var/svn/test";
    }
}

Without describing the problem this puppet snippet addresses, one might guess that I need to configure two subversion repositories, available via HTTP on the host “subversion.math.ohio-state.edu”.

The reason I absolutely *love* Puppet is the above code is all there is to this entire problem. Think about all the work that actually needs to happen to setup a subversion repository on a SSL enabled web server:

  • Install apache
  • Setup SSL certificates
  • Install subversion and dependencies
  • Setup apache virtual host with mod_dav_svn
  • Setup apache htaccess for access control to the repositry
  • Punch holes in the firewall (80, 443)
  • Create the blank repository with svnadmin
  • Ensure the repository is owned by apache
  • Ensure post-commit hooks are put in the right place and executable

Now, this is a lot of work, and I’ve already had the need to create new subversion repositories on other hosts. Because I’ve already modeled this problem in puppet, it’s trivial for me to bring up subversion servers on arbitrary hosts. I just re-use the block you see above.

Now, for the tricky part… Here are the modules that actually model the subversion repository in question.

Note that I’ve left out the classes which model other aspects of the host in question. For example, web::baseserver::ssl, firewall::input-port, and site-files::certificates (SSL Certs).

# Subversion Module.

class subversion::server inherits subversion {
    File {
        mode => 0640,
        owner => "apache",
        group => 0,
        require => [ User["apache"], Package["subversion"] ]
    }

    define webrepository ($path = false) {
        File {
            owner => "apache",
            group => "0",
            mode => 0660
        }
        $path_real = $path ? {
            false => "$name",
            default => "$path"
        }
        include subversion::server
        repository {
            "$name": path => "$path_real";
        }
        file {
            "$path_real":
                recurse => true,
                require => [ User["apache"], Repository["$name"] ];
            "$path_real/hooks":
                ensure => directory;
            "$path_real/hooks/bin":
                ensure => directory;
            "$path_real/hooks/bin/commit-email.pl":
                content => template("subversion/hooks/bin/commit-email.pl"),
                mode => 0770;
            "$path_real/hooks/post-commit":
                content => template("subversion/hooks/post-commit"),
                mode => 0770;
        }
    }

    include web::baseserver::ssl

    file {
        "/var/svn":
            ensure => directory;
        "/etc/httpd/htaccess/authz_svn.htaccess":
            content => template("subversion/htaccess/authz_svn.htaccess.erb");
        "/etc/httpd/htaccess/authz_svn.users":
            content => template("subversion/htaccess/htpasswd.mathsvn.erb");
    }
    web::vhost {
        "subversion":
            template => "subversion.conf.erb";
    }
    package {
        "subversion-perl":;
        "mod_dav_svn":;
    }
}

class subversion {
    $authz_svn_access_file = "/etc/httpd/auth_SVNAccessFile.math"
    $auth_svn_users_file = "/etc/httpd/auth_htpasswd.mathsvn"
    $svn_base_parent_repo = "/var/svn"
    Package {
        ensure => present
    }
    package {
        "subversion":;
    }

    define repository ($path = false) {
        $path_real = $path ? {
            false => "$name",
            default => "$path"
        }
        include subversion
        # Create a blank repository.
        exec {
            "svnadmin_create_$path_real":
                command => "/usr/bin/svnadmin create '$path_real'",
                require => [ Package["subversion"] ],
                creates => "$path_real";
        }
    }
}
Feb 06

DirectoryWe experienced a power outage today, caused by someone tripping the emergency power off relay to our server room. Unfortunately, emergency power off really means “power off” so our UPS did the right thing and completely cut power rather than fall back to battery backup.

It was a little bit stressful getting everything back up, but everything appears to be working fine now.

The one serious error message we ran into is the following, when bring our OpenLDAP server back up:

[root@ldap ldap]# /etc/init.d/ldap restart
Stopping slapd:                                            [FAILED]
Checking configuration files for slapd:  bdb_db_open: unclean shutdown detected; attempting recovery.
bdb_db_open: Recovery skipped in read-only mode. Run manual recovery if errors are encountered.
bdb(dc=math,dc=ohio-state,dc=edu): PANIC: fatal region error detected; run recovery
bdb_db_open: Database cannot be opened, err -30974. Restore from backup!
bdb(dc=math,dc=ohio-state,dc=edu): DB_ENV->lock_id_free interface requires an environment configured for the locking subsystem
backend_startup_one: bi_db_open failed! (-30974)
slap_startup failed (test would succeed using the -u switch)
                                                           [FAILED]
stale lock files may be present in /var/lib/ldap           [WARNING]

Fortunately, the solution to this problem is easy enough. Just run slapd_db_recover -v in the Berkeley Database directory.

cd /var/lib/ldap
slapd_db_recover -v

Finding last valid log LSN: file: 4 offset 4818337
Recovery starting from [4][4815752]
Recovery complete at Wed Feb  6 15:33:42 2008
Maximum transaction ID 80000ba7 Recovery checkpoint [4][4818337]

After that, slapd should startup just fine.

[root@ldap lib]# /etc/init.d/ldap start
Checking configuration files for slapd:  bdb_db_open: unclean shutdown detected; attempting recovery.
bdb_db_open: Recovery skipped in read-only mode. Run manual recovery if errors are encountered.
config file testing succeeded
                                                           [  OK  ]
Starting slapd:                                            [  OK  ]
Jan 23

PuppetNigel has posted slides from our Macworld 2008 presentation on Puppet.

Please see: Puppet Macworld 2008 Project

I’ll post additional information once I find out the details of distribution of any audio/video recordings taken during the presentation.