Setting up a Serial Console in Mac OS X

One of two things is generally meant by this, either using a Mac as the interface to a serial device (accomplished by running a terminal emulator program on the Mac), or using another machine to connect to the Mac over serial and accessing the shell provided by the Mac. This page is dedicated to the latter.

I first tried googling around, and I encountered a few references to this issue on various Mac forums. Many were quite old, and it appears that the process has gotten more difficult with more recent versions of OS X (at least Snow Leopard 10.5+). People have generally had more success with the Xserve OS X server versions. I was working with 10.6, non-server edition. The technique described here has been reported to work at least as recently as 10.9.1.

For completeness, here are some links to several of the sites I encountered, which should give you the basic idea of the problem:

Getting a serial port

The first step is to get an RS232 serial port on your Mac, which hasn't been built-in for a long, long time. You probably want a USB to serial converter, of which there are a variety. There are only a few common chips used in these, however, two of which are the FTDI series (driver here) and the Prolific PL2303 chipset (open source driver here, closed source driver here). Of the two, I found the open source PL2303 driver to be slow and observed occasional data corruption (I did not try the closed source driver but heard worse things). The FTDI driver performed perfectly.

If you have this working, a new device file should show up in /dev. Run ls /dev/tty.* to see what yours is showing up as. The next step is to test the serial port using some sort of terminal emulation program. I used goSerial, which happened to be the first one I found. It worked fine for my purposes, but better ones may exist.

Getting a getty running

In the Unix world, getty is the program that presents a login prompt. In earlier versions of Mac OS, one could add the appropriate line to /etc/ttys and have a prompt appear on a serial port. It appears that as of 10.6, this is no longer possible.

Instead, I found that creating a script to spawn the getty that is run by launchd does the trick. Note that running the getty on the command line appears to fail because the login_tty syscall can only be invoked by the init process (which on Mac OS is launchd). I therefore created a file called serialconsole.plist (name it as you wish) in /Library/LaunchDaemons/ containing the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>serialconsole</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/libexec/getty</string>
                <string>std.115200</string>
                <string>tty.usbserial-FTCCBMZ3</string>
<!--            <string>tty.PL2303-0000201A</string> -->
        </array>
        <key>KeepAlive</key>
        <true/>
</dict>
</plist>

Note that first bit sets the daemon name, the second bit sets the program arguments (one "string" per argument), and the last bit (keepalive) instructs launchd to restart the daemon when it exits (eg, the user logs out of the console). The first program argument is the getty program to execute, the second is the relevant line in /etc/gettytab that defines the desired serial port configuration (in this case the typical 115200 baud, 8 data bits, no parity, 1 stop bit; "8N1"), and the third argument to getty must be the name of the serial port device in /dev, which for the two devices I had were one of the two listed.

Using the program launchctl, you can manually reload these configuration files and restart daemons. Rebooting should also work. Note that in my case, the usb serial device appears in /dev after launchd attempts to run the getty, causing an error message to be logged to this effect in /var/log/system.log. However, the next time it attempts to restart it (within a minute or so), it should succeed. Check that log file for other errors.

Automatic login

For my application, I wanted a shell prompt to be immediately presented, without asking for username or password or displaying the login messages (last time logged in, new mail, etc.). To accomplish this, I needed to add the following entry to /etc/gettytab:

custom.115200:\
        :np:sp#115200:im=\r\n:al=<username>:lo=/usr/bin/quietlogin.sh:

as well as change the std.115200 to custom.115200 in the serialconsole.plist file. Note that this changes the login banner message to just a carriage return and newline, automatically logs in as user <username>, and spawns the login program /usr/bin/quietlogin.sh, which is a script I wrote (that wraps the default /usr/bin/login) containing the following:

#!/bin/bash

ARGS=("$@")

for ((i=0; i < $#; i++)); do
  ARGS[$i]=`echo ${ARGS[$i]} | sed 's/-f\(.*\)/-fq\1/'`
done

exec /usr/bin/login "${ARGS[@]}"

The reason this additional script was required was due to stupidity on the part of the login program that I observed. First, it seems to ignore the presence of a .hushlogin in the user's home directory requesting that no welcome messages be printed. Further, the -f argument passed to login must be first on the command line (before a "-q" which forces quite mode). (See the login man page for some of idea of what I'm talking about.) The above script performs this necessary munging.

Hiccups

Things not working? I'm not a mac user myself, so I can't offer too much help with debugging. Serial stuff in Mac OS appears to be of little interest to Apple, so things may change and break with each new version. If you learn something new during your own tinkering, let me know and I'll post it here. My email address may be derived from the URL of this document.

One person reported that replacing tty.<device name> with cu.<devicename> (thus using the "cu" call-out device instead of "tty" terminal device) made things work when they weren't otherwise. This was using somewhat unusual hardware as a Hackintosh, so that could be why.

Other thoughts

One reader suggested the possibility of using serial port emulation on a bluetooth connection as an alternative to USB-to-serial dongles. I would be curious to hear reports from anyone attempting this, particularly on behavior prior to bluetooth peering, during radio hiccups, and after reconnects (assuming the idea works at all). Before going too far down this road, do remember you can run IP over bluetooth and just use proper networking if your application permits.

Changelog

Last modified 1/5/2014