I searched and didn't find, and had to figure stuff out... I hope to save you the trouble.

Sunday, April 26, 2009

Run a TFTP server on a Mac

Apple has already included everything you need to serve files by TFTP from OS X 10.5. Put files in the directory /private/tftpboot, and start the service with:
sudo launchctl load -F /System/Library/LaunchDaemons/tftp.plist
Stop the service with:
sudo launchctl unload -F /System/Library/LaunchDaemons/tftp.plist

Run a DHCP server on OS X

DHCP will provide an IP address to clients when they need it. It's also the foundation for booting a machine over the network, but I won't get into that too much here.

The DCHP server (dhcpd) source works without any modification on OS X. The DHCP client is already part of OS X, so it's probably better to leave the version that Apple provided alone. To install only the server, we'll specify specific subdirectories.

Before you run it the first time, you need to create an empty leases file to keep it from complaining:
Can't open lease database /var/db/dhcpd.leases: No such file or directory --
check for failed database rewrite attempt!
sudo touch /var/db/dhcpd.leases
We'll setup a basic network and give clients the 192.168.1.70-100 range.

One reason you might want to run DHCPD is to host a network boot, which is called a PXE (Pre-eXecution Environment) boot. We'll throw a couple lines in our config to tell such machines to load the Linux network loader from the file pxelinux.0 on the host at 192.168.1.50 (which is the same Mac running the DHCP server - I'll discuss what I did with PXE in another post).

Here's a complete recipe to download, build and configure dhcpd for OS X:
curl -O http://ftp.isc.org/isc/dhcp/dhcp-3.1.3.tar.gz
tar xzvf dhcp-3.1.3.tar.gz
cd dhcp-3.1.3
./configure
make
sudo make install SUBDIRS='common dhcpctl omapip server'
sudo touch /var/db/dhcpd.leases
cat > dhcpd.conf <<END
default-lease-time 86400;
max-lease-time 604800;
ddns-update-style ad-hoc;
subnet 192.168.1.0 netmask 255.255.255.0 {
range 192.168.1.70 192.168.1.100;
filename "pxelinux.0";
next-server 192.168.1.50;
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option routers 192.168.1.1;
}
END
sudo cp dhcpd.conf /etc/
For my uses, I want this dchpd to serve clients hooked up to my MacBook's ethernet port, so I manually set an IP address that's on the appropriate network range (it's okay if it already has an IP address, this will just give it another one):
sudo ifconfig en0 192.168.1.50
When you start the dhcp server, you'll probably get a warning that there's no subnet declaration for en1 since we didn't configure anything for the AirPort network. That's okay, just ignore that wordy warning:
No subnet declaration for en1 (192.168.10.100).
** Ignoring requests on en1. If this is not what
you want, please write a subnet declaration
in your dhcpd.conf file for the network segment
to which interface en1 is attached. **

Start the dhcp server:
sudo /usr/sbin/dhcpd
Stop the dhcp server:
sudo killall dhcpd

Thursday, April 23, 2009

Remove old Eclipse workspace selections

Got stale workspaces in Eclipse's Workspace Launcher dialog? Edit the RECENT_WORKSPACES value in:

$HOME/.eclipse/org.eclipse.platform_3.3.0_1473617060/configuration/.settings/org.eclipse.ui.ide.prefs

(or change the platform to whichever version of eclipse you're using)

Modifying bash $PATH

Here's some functions to edit the $PATH without leaving crud:
function pathremove()
{
local P=:$1:; shift
while [ "$1" ]; do P=${P/:$1:/:}; shift; done
P=${P#:}; P=${P%:}
echo ${P}
}

function pathprefix()
{
local P=$(pathremove $@); shift
local N=""
for n in $@; do N=${N}:$n; done
echo "${N#:}:${P}"
}

function pathsuffix()
{
local P=$(pathremove $@); shift
local N=""
for n in $@; do N=${N}:$n; done
echo "${P}${N}"
}

Example usage:
export PATH=$(pathsuffix $PATH $HOME/bin /opt/bin)
export PATH=$(pathremove $PATH /sbin)
export LD_LIBRARY_PATH=$(pathprefix $LD_LIBRARY_PATH /opt/lib)

Automatically start ssh-agent

The following in my .bashrc starts ssh-agent and adds keys automatically whenever I open a shell, if it's not already running:

# Check PS1 to determine if it's a login shell
if [ "$PS1" ]; then
SSH_ENV=$HOME/.ssh/env-$HOSTNAME
if [ -f "$SSH_ENV" ]; then
source $SSH_ENV > /dev/null
fi

ps $SSH_AGENT_PID 2> /dev/null 1>&2
if [ $? != 0 ]; then
echo "$(date) : Starting SSH agent on $HOSTNAME" | tee -a $HOME/.ssh/agent.log
ssh-agent > $SSH_ENV
chmod 600 $SSH_ENV
source $SSH_ENV
ssh-add
fi
fi

Use tkdiff to view changes in subversion

To use tkdiff to view changed files in an svn repository put the following in a script called svndiff somewhere in your path, and make it executable:
#! /bin/bash
# Script to use a visual differ to view svn changes,
# supporting multiple files in one session

SVN=/usr/local/bin/svn
DIFF=tkdiff

args=""
sep=""
while read pair; do
args="$args $sep $pair"
sep=":"
done < <($SVN diff "$@" --diff-cmd echo | \
awk '/^-L/ {gsub(/\/tmp\/tmp/, "/dev/null"); print $(NF-1), $NF; next}')
$DIFF $args

Use python to decode base64 data

Whether it's data you copy/paste into the shell, or you want to use piped commands, you can decode base64 data from stdin and send it to stdout with:
python -c 'import sys,base64; base64.decode(sys.stdin,sys.stdout)'

Eclipse/PyDev on a Mac

  1. Download "eclipse classic" from http://www.eclipse.org/downloads
  2. Unpack the tarball, which will be an 'eclipse' folder
  3. Move the 'eclipse' folder to /opt (for the long-term, not just in your downloads folder)
  4. Make an alias from the Eclipse app into /Applications
  5. Run eclipse
  6. Go to Help > Software Updates > Available Software and click "Add Site..."
    enter: http://pydev.sourceforge.net/updates/
  7. Check the new site, including "PyDev for Eclipse" among its children
  8. Click the "Install..." button, and go through all the prompts including restarting eclipse
  9. Open Eclipse > Preferences > PyDev > Interpreter - Python
  10. Click the "New..." button
  11. Navigate in the finder open dialog to select /System/Library/Frameworks/Python.framework/Versions/2.5/bin/python2.5
    (tip: Use Cmd-Shift-G and paste in the directory, then select python2.5)
  12. Leave the default checked items for PYTHONPATH and click OK (you can edit it later if you need to)
When you go to create your PyDev project, don't get confused by the diabled "Next >" button... just click "Finish"

If you're doing an AppEngine app, you can add this to your PYTHONPATH:
/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine

Rollback a SVN change

Have svn make a diff of the previous revision to the revision you want to rollback, and reverse patch it.

Say you want to rollback r12345...
svn diff -r 12344:12345 | patch -R -p0

Modifying initrd with a Mac

When Linux boots, the bootloader starts the kernel with a parameter telling it what file to load into ram as the initial ramdisk. The initial ramdisk is used as the root filesystem until it's configured enough stuff and loaded enough drivers to mount the real root filesystem.

Normally the initrd is put together with the mkinitrd tool. But, initrd.gz is just a gzipped cpio archive, so you can use the gzip and cpio tools to extract and create it as well. To make sure permissions aren't messed up, run cpio as root (There are probably tools that will twiddle the bits in the cpio archive, but if you have root access then sudo is an easy answer).
mkdir initrd-root
cd initrd-root
gunzip -c ../initrd.gz | sudo cpio -imdv
Examine or modify the extracted contents as you like. Then you can put your modified initrd back together:
cd initrd-root
find . | cpio -ov -H newc | gzip > ../initrd.gz
But, wait... you said this could be done on a Mac...

If you're using Mac OS X, there's a slight glitch - the version of cpio that Apple ships doesn't support creating the necessary newc format archive.

To deal with that, we'll build our own cpio 2.9 from source, which almost works right out of the box. One small glitch is that current versions of gcc shipped with Xcode have a bug that results in:
ld: duplicate symbol _argp_fmtstream_putc in ../lib/libcpio.a(argp-fmtstream.o) and ../lib/libcpio.a(argp-help.o)
The problem is related to Apple's gcc improperly handling extern inline in C99 mode. Since we're just building a binary and therefore don't particularly care about C99, we'll just disable it with a configure flag.

Here's the download-and-build recipe:
curl -O http://ftp.gnu.org/gnu/cpio/cpio-2.10.tar.gz
tar xzvf cpio-2.10.tar.gz
cd cpio-2.10
./configure ac_cv_prog_cc_stdc=no
make
sudo cp src/cpio /usr/local/bin

Wednesday, April 22, 2009

Build squashfs tools for Mac OS X

To squash/unsquash a Linux squashfs filesystem on a Mac, you can build the squashfs tools.

A few modifications of the source are required for OS X:
  • The FNM_EXTMATCH flag doesn't exist in the fnmatch library on OS X. This just means that you can't use glob patterns with mksquashfs.
  • The constants to figure out how many CPUs the machine has are in the sysctl header instead of the sysinfo header.
  • The C99 compiler has different semantics for the inline keyword, so inline functions should also be static.
  • Instead of llistxattr(), lgetxattr(), lsetxattr(), Apple uses a XATTR_NOFOLLOW flag on the non-link equivalent functions.

Here's a complete download-and-build recipe:
curl -OL http://iweb.dl.sourceforge.net/project/squashfs/squashfs/squashfs4.2/squashfs4.2.tar.gz
tar xzvf squashfs4.2.tar.gz
cd squashfs4.2/squashfs-tools

sed -i.orig 's/FNM_EXTMATCH/0/; s/sysinfo.h/sysctl.h/; s/^inline/static inline/' mksquashfs.c unsquashfs.c

cat <<END >> xattr.h
#define llistxattr(path, list, size) \
  (listxattr(path, list, size, XATTR_NOFOLLOW))
#define lgetxattr(path, name, value, size) \
  (getxattr(path, name, value, size, 0, XATTR_NOFOLLOW))
#define lsetxattr(path, name, value, size, flags) \
  (setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW))
END

make
sudo cp mksquashfs unsquashfs /usr/local/bin

Usage notes:

The filesystem you're unsquashing probably has root-owned files in it, so you'll have to use sudo to run unsquashfs as root.

The limit of open files is somewhat low on a Mac, so you'll get a bunch of errors if you try to unsquash a whole filesystem:
write_file: failed to create file squashfs-root/path/to/file, because Too many open files
Just change the limit in the shell before running unsquashfs:
ulimit -n 2000
sudo unsquashfs filesystem.squashfs

There seems to be a bug when drawing the progress bar that causes Floating point exception, which you can workaround by using the -n flag.

Followers