This post first appeared on the Big Nerd Ranch blog.
TL;DR: JSConf 2015 was awesome, and you can open a shell to the Parrot Rolling Spider’s Linux innards with screen /dev/tty.usbmodem…
May is beach month. Every year, I head to the Sunshine State for a fair dose of UV rays to counteract an epidemic skin disease (the treatment usually involves sunburn). Afternoon swims are among the most palatable treatment options, although poolside activities often interrupt it. It takes a certain amount of agility to dodge incoming gulls, beach balls and Node.js-controlled boats.
This year marks my second visit to JSConf’s paradisiacal backdrop on Amelia Island, and is particularly memorable—the talks demoed unusual applications and dazzled the imagination. I also gave my first JSConf talk on replacing native local variables with prototypes.
But I think everyone would agree: the swag item this year blows all the t-shirts and branded beach balls out of the [beach] water.
So. Many. Drones.
I thought the twenty or so AR.Drone quadcopters at last year’s NodeCopter event was unnerving. This year, the conference was swarmed by 700 Parrot Rolling Spider quadcopters.
Despite the drone’s apocalyptic appearance, the human finger inflicts more damage on the tiny plastic props than it does in return. But at 1400 LED eyes and 2800 props strong, the same cannot be said for terrorized house pets and seagulls.
Thanks to Chris Williams’s pre-conference homework, I was able to pilot my drone from my laptop with vim-style controls and an Xbox 360 controller. That’s pretty awesome, but a little boring, considering we did that last year.
I think my four-winged friend can do so much more: I want to run my own software directly on the drone to make something truly autonomous! I figured there would be docs and posts detailing how this could be done. However.
At Georgia Tech, the CS program had a minor obsession with the original AR.Drone, so I've worked with it on a few occasions. Interestingly, it ran Linux, so I suspected that the Rolling Spider also runs Linux. If that was the case, I might be able to open a terminal and peek into the drone’s guts!
With the guidance of my proc killin’ colleague Steve Sparks, we took a historical detour to reminisce about the days of 56k dial-up modems. Coincidentally, modems made an appearance at JSConf, so I had some background.
In the distant past, operators would relay text over telephone lines with keyboards. Similar to dial-up modems, different keys would be converted to a 5-bit code called Baudot code and transmitted over wire. Baudot code was later standardized as ITA2, the predecessor to 7-bit ASCII. The Teletype Writer, as it was called, was later modified for communicating with computer terminals. Nowadays, most Linux devices listen for serial connections (directly through COM, or emulated over USB) and communicate to TTY (Teletype) sockets.
On UNIX machines, TTY devices mount under
/dev/tty*. Plugging in my drone over USB, two new entries show up:
$ ls -1 /dev ... /dev/cu.usbmodem1d113 ... /dev/tty.usbmodem1d113 ...
The drone exposes a TTY socket over USB. This is thanks to the
getty utility running on the drone, which listens for incoming connections and forwards traffic to the machine’s virtual console.
If the drone is listening, my laptop needs to request and display the login shell. Like yesteryear’s kitchen appliances, the venerable
screen terminal emulator can do anything: it even knows how to communicate with a TTY device over serial ports:
$ screen /dev/tty.usbmodem1d113
And I’m in!
BusyBox v1.20.2 (2015-03-06 17:53:39 CET) built-in shell (ash) Enter 'help' for a list of built-in commands. #
It doesn’t require a username and password, so it’s probably running root in single-user mode:
# whoami /bin/sh: whoami: not found
Wow, seems a bit short on conveniences. Let’s try:
# echo $USER root
That's it! One slight problem though: the drone disconnects after 60 seconds. It’s exasperating to explore the drone’s filesystem one minute at a time, so maybe it’s time for a different approach.
Looking for Clues
While upgrading my drone, I peruse the mysterious
rollingspider_update.plf file, which presumably wraps the core firmware update. Since the drone is Linux based, there should be several shell scripts scattered in the update file that could hint at turning off the auto-sleep. Unfortunately, none of the open source AR.Drone PLF extractors work with the Rolling Spider variant.
Time to analyze the update myself. Maybe the update is just a
.tar.gz with a different file extension:
$ file rollingspider_update.plf rollingspider_update.plf: data
Nope. Let’s try
strings rollingspider_update.plf, which finds plain text sequences terminated by the NUL character (typical in binaries).
$ strings rollingspider_update.plf [gobbledygook and ༼ ༎ຶ ෴ ༎ຶ༽]
Sigh; a few readable bits turn up, but most of the file looks like compressed gobbledygook.
strings doesn’t know how to extract compressed strings, but Binwalk does. Binwalk analyzes binary files and extracts resources, like images and text, into their respective files by searching for tell-tale signatures.
$ binwalk --extract rollingspider_update.plf
Binwalk spits out a directory of arbitrarily named files, most of which are binaries.
$ ls -1 _rollingspider_update.plf.extracted 105F5E.zlib 107CC1.zlib 10865F.zlib 10D400.zlib 1134B1.zlib 114918.zlib ... 525E34 529C28 52A63B.zlib 52A761.zlib 52ACE.zlib ...
However, a fair number of files without the
.zlib extension are scripts, so I explore all the files starting with the
#!/bin/sh shebang. One in particular stands out:
$ cat 43DDA4 etc/init.d/rcS[gobbledygook]#!/bin/sh # IP_ADDR - this target IP address using CIDR notation: # <target-ip>/<target-bitmask> # # For example: echo init started... source /bin/delos_shell.sh ... echo "Launching Dragon" | logger -s -t "rcS" -p user.info DragonStarter.sh -out2null & # Start the FSM managing USB Modes (delos_monitor_vbus_script.sh)& echo end init...
Looks like the
/etc/init.d/rcS script. On a Linux system,
rcS is run after the kernel has booted into single-user mode: it typically initializes some services and mounts the filesystem before kicking off the main program (e.g., the piloting software). The Rolling Spider happens to start up the
getty program (which allows us to shell in),
rcS. More on the
Wakey Wakey, Spidey
After digging through the update file, I’m ready to double down on the 60-second time limit. On a Linux device, “sleeping” likely means the system is shutting down entirely. So if it shuts down, maybe we can search the extracted files for scripts that have the shebang and mention
$ comm -12 <(ag -l --search-binary shutdown) <(ag -l --search-binary '#!/bin/sh') ... 7B6170 ... $ head -n1 7B6170 bin/device_monitoring.sh[gobbledygook]#!/bin/sh
Sure enough, one of the scripts,
/bin/device_monitoring.sh, appears to track device state (flying, connected over USB, etc.) and notes when 60 seconds of inactivity have passed. The bottom of the script is especially pertinent:
if [ ! -f /tmp/.bleproxy.ftpinprogress -a ! -f /tmp/.shell.userconnected ]; then /bin/delos_shutdown.sh fi if [ -f /tmp/.bleproxy.ftpinprogress ]; then (/bin/device_monitoring.sh)& fi
It looks like
shutdown gets called after 60 seconds, unless
/tmp/.shell.userconnected exist on the filesystem. So, let’s try logging back into the drone and run:
# touch /tmp/.shell.userconnected
Success! Now the drone will stay on so we can explore indefinitely. It’s a bit inconvenient to run this every time we connect to our drone. It’s also irritating to run
screen in the terminal since we lose our nice scrollback, but we can remedy both issues.
Most terminal emulators let you specify “Profiles,” so let’s make one for the drone. I use iTerm2, but the standard terminal bundled with OS X supports profiles as well.
In your preferences, create a new Profile (I named mine “Drone”) and override the login shell. By default, this will be your system’s default shell (like Bash or ZSH), but you can specify
screen normally connects to the computer’s virtual terminal, but we can pass in our drone’s TTY instead.
To connect to the drone now, I can open the “Drone” profile and enjoy native scrollback controls. Finally, I want to run
touch /tmp/.shell.userconnected as soon as the shell is up, so add that to “Send text at start.”
Now it couldn’t be more convenient to turn those fearsome red eyes to a more interesting orange:
This isn’t the entire story. In reality, I didn’t figure out the easy-peasy USB method until after trying to connect via Bluetooth. In my next post, we’ll take a detour into virtual machines and the Linux Bluetooth stack for more interesting ways to connect to and control embedded Linux devices.