Saturday, January 10, 2009

OS X: How to detect whether you're on battery from the shell

Sometimes you need to determine programatically whether a computer is on battery or on AC. For example, I have a backup script that takes hourly snapshots of my home directory on my MacBook Pro (which I hopefully will post about at some point). However, I don't want the script to run if I'm on battery. Ideally, launchd, which kicks off the script, could give me this option, but I don't think it can. I'll have to let the script make the decision when it gets executed.

If I were writing the script for Linux, I'd have it look through /proc/acpi/ to check the state of the battery. There's no /proc in OS X. There is, however, a power management command, pmset.

If I run pmset -g while on AC, I get:

Currently drawing from 'AC Power'
-InternalBattery-0 100%; AC attached; not charging

If I run it while on battery, I get:

Currently drawing from 'Battery Power'
-InternalBattery-0 100%; discharging; (no estimate)

I simply have my script grep the output from pmset for "discharging":

checkBattery()
{
local ret=0;
if $PMSET -g batt | $GREP -q discharging;
then
ret=1;
fi
return $ret;
}

See the pmset(1) manpage for more info.

Friday, January 09, 2009

Converting a .dmg to a .iso

I have an operating system install image that I need to install in VMware Fusion. The problem is the install image is a MacOS disk image file, or .dmg. I need a DVD image, or a .iso, in order to boot the VM and do the install.

It turns out that the Mac OS X command hdiutil to convert the .dmg to a .iso:

hdiutil convert .dmg -format UDTO -o .iso

See the hdiutil(1) manpage for more info.

Thursday, January 08, 2009

How to sudoedit non-interactively

Okay, this one's a bit esoteric, but I think it's pretty cool. How do you use 'sudoedit' non-interactively such as from a script? Just a brief background about sudo: sudo is an authentication mechanism in Unix & Linux that allows unprivileged users to run specific commands (as defined by the system administrator) with root privileges without having the root password. This has several advantages over logging in as root:
  • Users can have specific, limited set of root privileges without having the entire set of root privileges.
  • Users use their own password, so the root password doesn't have to be shared. If a user's sudo privileges are revoked, the root password doesn't have to be reset.
  • Each use of sudo is audited per user, so that each time sudo privileges are invoked, there is an event in the system logs that identifies the specific user and the command they ran.
Sudoedit is a command that is related to sudo. It lets users edit files that normally only root can edit, such as system configuration files. However instead of using "sudo" followed by the editing command, the user runs "sudoedit filename" and sudo invokes the user's default editor, letting them edit the file.

So, what if you want to make changes to a system file via a script, and the only access you have to the file is via sudoedit? It isn't useful to have the script call sudoedit and then bring up vi for you to manually make the changes.

Well, the way sudoedit works, is that sudo makes a temporary copy of the file you want to edit, then calls your default editor, giving it the name of the temp file as the first argument. So, say your default editor is "fooedit". You run "sudoedit /etc/systemfile" and sudo makes a copy of /etc/systemfile to /tmp then runs "fooedit /tmp/tempfile". Your changes are saved to the temp file. When your editor exits, sudo copies the temp file over the original, and then removes the temp file.

From your main script, stage the pre-edited version of the system file, then set your default editor to a custom script that will copy that staged file over the temp file. When it exits, sudo will copy the temp file into place as normal.

Your script would go like this:


#!/bin/sh

export EDITOR="/bin/sh ./editor.sh";
PRE_STAGE=/tmp/stage.tmp;

echo "my new config"> $PRE_STAGE;

sudoedit /etc/config;

/bin/rm $PRE_STAGE;

exit;

Your custom script would look like:


#!/bin/sh
CAT=/bin/cat
PRE_STAGE="/tmp/stage.tmp"

$CAT $PRE_STAGE > "$1";
exit;


This script just cats your pre-staged file over whatever sudo passed in as the first argument ($1).

The only interaction you'll have to do is type your password for sudo, if you haven't already done so in the last few minutes (sudo temporarily remembers your authentication).