Tips and pointers for using CFEngine on Mac OS X. The following subsections cover details of CFEngine particular to major Apple releases, and issues with compiling CFEngine manually.
- 
        Compiling CFEngine on Mac OS X
      
- 
        CFEngine on Mac OS X 10.4 (Tiger)
      
- CFEngine on Mac OS X 10.3 (Panther)
Mac OS X Classes
CFEngine sets a darwin class, but nothing to distinguish Mac OS X 10.2 from 10.3, nor Mac OS X from Darwin. Maintaining a source code patch to do so would be complex and time consuming; the following shows initial work on identifying the operating systems apart.
groups:
  darwin::
    macosx = ( FileExists(/System/Library/CoreServices/Finder) )
    # TODO pipes not supported, so would need wrapper scripts or other
    # means of distinguishing 10.x apart
    #macosx_10_2 = ( "/usr/bin/sw_vers | fgrep 10.2" )
    #macosx_10_3 = ( "/usr/bin/sw_vers | fgrep 10.3" )
    # KLUGE 10.3 first with Postfix, 10.2 had Sendmail. Will need to
    # reconsider what to do when 10.4 comes out.
    macosx_10_2 = ( FileExists(/System/Library/StartupItems/Sendmail) )
    macosx_10_3 = ( FileExists(/System/Library/StartupItems/Postfix) )
import:
  macosx::
    cf.macosx
System Configuration
Useful configuration commands include pmset, nvram, softwareupdate, and diskutil. Install the Apple Remote Desktop Client software to gain access to the systemsetup and networksetup commands.
control:
  ard_dir = ( /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Support )
links:
  macosx_10_2::
    /usr/sbin/networksetup ->! $(ard_dir)/networksetup-jaguar
    /usr/sbin/systemsetup ->! $(ard_dir)/systemsetup-jaguar
  macosx_10_3::
    /usr/sbin/networksetup ->! $(ard_dir)/networksetup-panther
    /usr/sbin/systemsetup ->! $(ard_dir)/systemsetup-panther
Mixed directories under /Users.
Another problem is dealing with user home directories; Apple uses /Users instead of the more common /home used on other systems. Also, Apple mixes /Users/Shared in with the other user home directories, which complicates matters, as all directories under /Users are not actually user directories.
The easiest way to deal with this would be to remove /Users/Shared, and recreate it elsewhere if required. Then, for cross platform needs, use a $localhome variable to track where the local system home directory is.
control:
  darwin::
    localhome = ( /Users )
  openbsd::
    localhome = ( /home )
tidy:
  # r* services insecure, remove configuration for them
  /etc pat=hosts.equiv age=0 r=1
  $(localhome)/* pat=.rhosts age=0 r=1
Automatic Updates
Use the softwareupdate command to install updates from the command line. Be sure to set the COMMAND_LINE_INSTALL=1 environment variable to avoid any prompts during the run. Older Mac OS X systems (10.1 and below?) may not have the softwareupdate utility.
#!/bin/sh
export COMMAND_LINE_INSTALL=1
if [ -f /var/log/auto-update.log ]; then
  /bin/mv -f /var/log/auto-update.log /var/log/auto-update.log.old
fi
(
  /usr/sbin/softwareupdate --install --req
  /usr/sbin/diskutil repairPermissions /
) >/var/log/auto-update.log 2>&1
if /usr/bin/grep -q restart /var/log/auto-update.log; then
  /sbin/shutdown -r now restart required due to system update
fi
Updates could be installed automatically (via a shellcommands in CFEngine or a cron job), though ideally should be tested first on one or more test systems before general distribution. Automatic reboots may cause data loss, if a user with unsaved data is still logged in when the reboot occurs.
To allow non-administrator users to run updates, create a script with the above command, then allow access to it in the sudoers file. Either use visudo, or edit the sudoers file directly via CFEngine.
ALL ALL=NOPASSWD: /usr/local/sbin/run-sw-updates
Either have the users run the update from the command line, or create a wrapper that the users can click on to call the shell command in question:
-- save as AppleScript command via "Script Editor" application
on run
  set theResult to display alert ¬
    "Check for updates? This may require a reboot!" as warning ¬
    buttons {"Cancel", "Restart"} ¬
    giving up after 120
  if button returned of theResult is "Restart" and ¬
    gave up of theResult is false then
    do shell script "/usr/bin/sudo /usr/local/sbin/run-sw-updates"
  end if
end run
