Apple Software Update options

Software Update

UPDATE: It is now possible to use munki to allow non-admin users to install Apple Software Updates — either from your server or Apple’s. See here for more information.

At work, I’ve been working on a solution for handling Apple Software Updates for non-administrative users. Each approach I’ve tried has serious obstacles. Here’s a brief history of what I’ve tried and what worked and what didn’t.

First, a little background: Apple provides a Software Update Server as part of Mac OS X Server. This allows you to cache Apple’s updates on a local server for more efficient use of Internet bandwidth, and more importantly, a local Software Update Server allows you to choose which items you want to make available to your organization — for example, you can choose to not make an update available to machines in your organization until after you’ve finished testing it in your environment.

But hosting a local Software Update Server does not solve a fundamental problem: how to get the updates onto a machine without requiring administrator privileges for the machine’s user(s). Hosting a local Software Update Server is no different than relying on Apple’s servers in this regard; someone must authorize Software Update.app with administrative credentials.

So if you want to use Software Update to distribute updates to your machine, this means either providing all your users with administrative credentials, or having admins visit each machine. It would be better if we could find a way to install updates without requiring a user to type in an admin password.

If you are using a third-party solution for installing software and handling non-Apple updates, something like radmind, LANrev, FileWave, or Casper, these products may have their own mechanisms for handling Apple Software Updates. If they don’t, you always have the option of downloading the updates from Apple and importing them into your management system just as you would any third-party software.

But a downside to doing this is that you may lose the logic Apple uses to determine which updates are applicable to each machine. You may find yourself having to replicate this logic in some way. The larger the number of unique hardware and software configurations you have, the harder and more tedious this becomes. So you may want to find a way to leverage Apple’s tools.

The obvious first approach: as root (via cron, launchd, ARD or ssh), run the command-line Software Update tool:

/usr/sbin/softwareupdate -i -a

This is simple, but has several problems:

  1. The only safe time to do this is when no-one is logged in, since a restart may be required, or applications that are in use might be updated. But if you do it when no-one is logged in, and you provide no user feedback as to what is happening, you run the risk that the user will shutdown, restart, or log in while the process is happening. The first two could cause you to have an unbootable machine; a user logging in could be a problem if a restart is required or the user attempts to use applications that are being updated.
  2. Since a user might be waiting to use the machine while this process is happening, ideally you’d want to have some sort of progress indicator, and also you’d like to do the downloads separately from the install so that the time your users are forced to be logged out is minimized. But /usr/sbin/softwareupdate‘s output is almost impossible to use to construct meaningful progress info, and it has no way to download items in advance.
  3. You are thinking “But what about softwareupdate -d -a? Doesn’t that download all the available updates?” Why yes, it does. Unfortunately, there’s no way to tell softwareupdate to use those downloads, if you later run softwareupdate -i -a, it will download them all over again.

If you are willing to make the user wait for the entire download and install process while the machine is logged out with no real progress feedback, then this approach might work. If you have a bunch of desktop machines that will be on (or can be set to wake up/power up) in the wee hours of the morning, this approach might work, as the lengthy updating can happen when no-one is around. If, on the other hand, you have a large number of laptop machines, this approach is less likely to be accepted by your users. They’ll have to participate in the updating of their machines, and won’t like having no indication of how long an update might take. They are more likely to get annoyed or impatient and simply turn off the machine in the middle of the update, because it may appear that the machine is locked up.

So that brings us to the next approach: again, with a script running as root, use softwareupdate -d -a to find and download the available updates, then use /usr/sbin/installer to install them. This seems promising, because /usr/sbin/installer gives reasonable progress feedback that you could then pass to the user.

Unfortunately, not everything downloaded via softwareupdate -d -a is in a format that /usr/sbin/installer knows how to install. And so far, I haven’t been able to preflight these items to know in advance what can be successfully installed and what cannot — you just have to try and see if /usr/sbin/installer throws an installation error back at you. So unless we can figure out how to detect and deal with the “broken” downloads, this is not an ideal solution, as there will be updates that are not installable via this method.

Now for the third approach.

It is possible to trigger the process that downloads updates in the background into /Library/Updates/ — the same one that, when updates are available, then opens the Software Update.app and prompts you to install them. (On Snow Leopard, this is essentially the same thing as running softwareupdate -d -a; on Leopard, softwareupdate -d -a does something different, downloading them into the Downloads directory of the current user.)

Once these items are staged in /Library/Updates, you can re-write the /Library/Updates/index.plist and touch a file to tell Software Update to install these upon restart; this is a variation of the built-in behavior when you use the Software Update app and choose to install items that require a restart – you are logged out, the items are installed, and then the machine is restarted.

Here’s a shell script that gets the basic steps done:


#!/bin/sh

defaults -currentHost write com.apple.SoftwareUpdate AgreedToLicenseAgrement -bool YES
defaults -currentHost write com.apple.SoftwareUpdate AutomaticDownload -bool YES
defaults -currentHost write com.apple.SoftwareUpdate LaunchAppInBackground -bool YES

#Take away execute & read rights so Software Update doesn't open during a user session
chmod 700 /System/Library/CoreServices/Software\ Update.app
#Check for new updates
/System/Library/CoreServices/Software\ Update.app/Contents/Resources/SoftwareUpdateCheck
#Put execute and read rights back
chmod 755 /System/Library/CoreServices/Software\ Update.app

#This sets up all the files so that it will install on restart
installarray=`defaults read /Library/Updates/index ProductPaths | grep -v "[{}]" | awk -F "=" '{print $1}' | grep -o "[^\" ]\+"`
defaults write /Library/Updates/index InstallAtLogout -array "$installarray"
touch /var/db/.SoftwareUpdateAtLogout
chmod og-r /var/db/.SoftwareUpdateAtLogout

Leveraging this has several attractions: you are using Apple’s processes and logic for installing updates; you get progress feedback for free, since you are using Apple’s tools, and the updates can be downloaded in advance, minimizing the time required at logout to install the updates.

There are some drawbacks:

  • You have to trigger a restart to initiate the install; even for items that don’t require a restart.
  • When the restart is triggered, a dialog appears telling the user there are updates to be installed and giving them two choices “Restart”, or “Install and Restart”. While it’s good to have the choice, it’s an additional bit of noise if you’ve already asked them to restart to install the updates.
  • And the big one: there’s no clean, Apple-supported way to programmatically trigger the install if you are already sitting at the loginwindow.

    If a user is logged in, you can do this:

    osascript -e 'tell application "System Events" to restart

    But if no user is logged in, the above doesn’t work. If you do shutdown -r now, the machine just restarts without installing. If you click the Restart button in the loginwindow, you are prompted to allow the installs to happen, which is what we want. It turns out that in Leopard and Snow Leopard, after a little setup, we can do just that programmatically with:

    osascript -e 'tell application "System Events" to tell process "SecurityAgent" to click button "Restart" of window 1'

    This doesn’t work, obviously, if the administrator (or user!) has disabled the Restart button from appearing in the loginwindow, so it’s not particularly robust, but it’s the best we have right now.

A fourth approach:

A script running as root launches the Software Update.app:


#/bin/sh
/System/Library/CoreServices/Software\ Update.app/Contents/MacOS/Software\ Update

Ideally, you’d use a launchd job that watched a path writable by non-privileged users. When that path was modified, the launchd job would fire, launching Software Update as root.

Advantages:

  • Once Software Update is running as root, the user can download and install anything Software Update makes available without having to enter administrative credentials.
  • You’re using even more of Apple’s code and relying less on your own, making this a robust solution.

Possible disadvantages:

  • The user can choose to not install some items — this could make it more difficult to apply Security Updates in a timely manner
  • Since Software Update.app is running as root, there are potential security risks; it might be possible to use a flaw in Software Update.app to can root privileges elsewhere.
  • This approach doesn’t help you install updates when no-one is logged in; it only works with user involvement and cooperation.

So there you are. Four ways to get Apple Software Updates installed on machines without requiring your end users have administrative credentials.

No one approach works in all situations, but perhaps one of these approaches will work for you.

Apple Software Update options

23 thoughts on “Apple Software Update options

  1. Daniel says:

    I’ve struggled with this as well, but I have not done the work to automate the process like you have. For one thing, I only have a few Macs to update so it’s been easier to just login to ARD and walk through the updates myself.

    More to the point. It is a real shame that Apple has not done a better job of making this process more administrator friendly. I seem to remember from my Windows days that group policy update tool made deploying updates a breeze…then again, I was not wearing the admin hat back then, so maybe that has it’s problems too?

    Your requirements and the problems that you mention above seem pretty obvious, and I would think they would have been thought of when the OS X software update system was designed. Why can’t Apple make this easy? It doesn’t seem to be rocket science.

  2. anon says:

    Few pointers here since I think we all hate this.

    1.) Google has its update engine. I dislike the google software update installed by their products (disable launched), but the update-engine framework is on google code. its very easy to script/work into your own packages

    2.) Check out munk on google code. this looks like the best bet so far.

  3. Our problem is that we haven’t found a way to serve out iPhone firmware updates via our local Software Update Server because they come in via iTunes instead of the system software update service.

    Anyone know of a solution?

    -Mart

  4. Joe says:

    #!/bin/sh

    #STEP1: Disable logins
    dseditgroup -o edit -d netaccounts -t group com.apple.access_loginwindow

    #STEP2: Tell the users they can’t login and why
    defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText -string “LOGIN TO THIS WORKSTATION HAS BEEN TEMPORARILY DISABLED. SOFTWARE UPDATES ARE BEING INSTALLED. SYSTEM WILL AUTOMATICALLY REBOOT WHEN FINISHED.”
    killall loginwindow

    #STEP3: update the software
    softwareupdate -ia

    #STEP4: re-enable logins
    dseditgroup -o edit -a netaccounts -t group com.apple.access_loginwindow

    #STEP5: Remove text from login window
    defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText -string “”

    #STEP6: unload and disable the job
    launchctl unload -w /Library/LaunchAgents/edu.college.updatejob.plist

    #STEP7: reboot
    sudo reboot

    exit 0

  5. Mike says:

    We are trying to do this at a University I work at. Option 3 looks the most promising as we have faculty we do not want to give administrative access to. Have you successfully gotten your script to work on the 3rd option you described?

  6. Option 3 will work as long as it’s acceptable to always have a user initiate a software update session – I gave up trying to reliably trigger the update when no-one was logged in.

    In munki (http://code.google.com/p/munki) I’m currently using something closer to Option 2; I’ve so far been able to successfully use /usr/sbin/installer to install everything downloaded by software update. I still worry that this won’t work for every update, though.

    1. Mike says:

      “as long as it’s acceptable to always have a user initiate a software update session”

      I am not quite sure I understand what this means. Initiate the update session by restarting the machine? Because as far as I know, users who do not have administrative privileges do not get the software update app to pop-up. They have to manually go to Apple -> Software Update…

      You really have a lot of good ideas here and it looks like you have advanced far past what we need to do. We just really want to get away from giving users administrative privileges but want to maintain patched machines. Thanks very much in advance for any help you can throw my way.

      Mike

      1. “as long as it’s acceptable to always have a user initiate a software update session”

        “I am not quite sure I understand what this means. Initiate the update session by restarting the machine?”

        Yes. Here’s how Option 3 works: the script (running as root) sets up the downloaded updates so they’ll install on restart. But only a user-initiated restart will do — “/sbin/reboot” and “/sbin/shutdown -r now” bypass the Software Update install mechanism.

        So you have to notify the user that updates are available and prompt them to restart. Assuming they cooperate, they then will see a dialog telling them there are updates to be installed and giving them two choices: “Restart” and “Install and Restart”. So now they have another chance to thwart the update by clicking “Restart”.

        -Greg

  7. Mike says:

    Greg thank you for the quick response. Do you set up the script to run on login? Or send it via ARD? Or how exactly do you get the script running? Thanks again, I have found your blog to be a great help and I appreciate you taking the time to answer my menial questions.

    1. I would think you’d want to run it via launchd as a periodic task, or vi cron or periodic. You’d want to run it periodically, but as root, so running it at login is no good.

      1. Mike says:

        I am not really versed in launchd how would I go about getting launchd to run this script (script.sh) every 36 hours? Thanks again.

  8. If you aren’t comfortable with launchd I’d recommend you not use it yet, and instead leverage an existing system facility. Later you can figure out launchd.

    1) Make sure the script actually runs and does what you expect.

    2) Mark it as executable – chmod 755 /path/to/script

    3) Copy it to /etc/periodic/daily/ or /etc/periodic/weekly/ to run it either daily or weekly:

    sudo cp /path/to/script /etc/periodic/daily/501.softwareupdate

    It will now run as part of the daily or weekly periodic tasks.

    -Greg

    1. Mike says:

      Sounds good. I just imaged a machine and am getting ready to set up the script for testing. Thanks very much once again.

  9. Mike says:

    Is there a set time that daily will run this script? Or does it vary from system to system? Also can you tell me where to find some good information on launchd? Apple’s website has some on it but not too much, and the book I have Mac OS X Deployment 10.5 only covers it for about two pages.

  10. Nick says:

    Here’s how I am doing it…

    I have a script that checks for software updates. I am running an internal software server so only the updates I want to deploy hit the workstations. So all updates are always set to install. If software updates are available I set a logout hook to kick off another script that installs the updates. I have a notification window that let’s the users know the updates are installing.

    I keep going back and forth between login and logout hooks, as I have found end users closing their MacBooks even when they are told not to, because it’s the end of the day and they want to leave.

  11. Mike says:

    IS anyone willing to share any code they are using to automate these updates? I have had no luck trying a variety of different methods and we really need to get a solution in place so that we can update machines centrally for users that have no administrative privileges. We have a Software Update Server, but ARD is not cutting it when it comes to trying to update the machines. Ids anyone willing to lend a hand. Any help would be greatly appreciated.

    1. Nick says:

      Sorry I didn’t see this I wasn’t getting updates about this thread through email for some reason. I will gladly share my scripts if you want them but truthfully I have moved on to Casper for full system management which includes adminless software update that I can even run in the background while the user is working.

      -Nick

  12. […] vs StarDeploy) Munki / MunkiReport / Simian StarDeploy Deploying Microsoft Office for Mac 2011 Apple software update options Munki-specific Getting started with Munki Some pointers on repackaging software Munki and Adobe […]

  13. Majkel says:

    Have anyone know the solution for the problem “When the restart is triggered, a dialog appears telling the user there are updates to be installed and giving them two choices “Restart”, or “Install and Restart” While it’s good to have the choice, it’s an additional bit of noise if you’ve already asked them to restart to install the updates.”?
    The users shouldn’t have opportunity to discard installation.

  14. I am part of a group of volunteers that is starting a new scheme in our community in Tucson. One of the community projects that we are about to start relatates to your blog, and therefor some of the information here is of value for us and I just wanted so say thank you for that.

Comments are closed.