Minority Opinions

Not everyone can be mainstream, after all.

Therapist Hacking

leave a comment »

As intriguing as Dwarf Fortress is, it’s almost unplayable on its own.  The two main utilities for improving it are Dwarf Therapist and DFHack.  The former offers a convenient interface for managing job assignments, while the latter fixes various bugs.  (Both also allow you to cheat in various ways, but that’s beside the point.)  They mainly accomplish these feats by fiddling with the memory of a running Dwarf Fortress process.

Linux frowns upon letting processes fiddle with each others’ memory, because it’s usually a security risk.  Almost no legitimate processes want to.  DFHack works around this by swapping a certain library out from under Dwarf Fortress, causing DF to load the DFHack code into its own process space.  Dwarf Therapist, on the other hand, simply tries to find a running DF process.  For a time, that worked, as long as both programs were run by the same user; however, more recent Linux distributions have closed even that loophole.  This forces the user to do one of three things:

  1. Run Dwarf Therapist with root privileges.
  2. Re-open the ptrace loophole, either at boot or each time you run Dwarf Therapist.
  3. Run Dwarf Therapist as a parent process of Dwarf Fortress.

The third is least risky, but the Therapist doesn’t run any programs itself.  This has required me to use launch scripts that exec themselves into it after launching DF in the background.  All worked well, until I tried to stuff DFHack into the equation.

DFHack uses a command prompt.  On Windows, it will open its own, but the Linux version expects to already have one available.  Sadly, when backgrounded by my launch script, DFHack had only limited access to the terminal from which it was launched.  In particular, it couldn’t read any input.

Fortunately, I found a nasty-looking set of file descriptor redirections that lets DFHack bypass the normal job-control trickery and use the terminal as its own command prompt.  But only while Dwarf Therapist remains open and in the foreground.  Background it, or close it, and DFHack gets very confused, because it can’t read anything anymore.

So, my launch script grew more complicated, to launch a terminal emulator just for DFHack’s use.  Given that Dwarf Fortress and Dwarf Therapist already open their own windows, this should always work.  I just need to find the right terminal command.  But if that fails, I can always fall back on the redirection goofiness, and hope I remember to close the Therapist first.

#!/bin/bash
# Launch Dwarf Fortress under DFHack and Dwarf Therapist
# Tricky, because DFHack needs the console, but DT needs to be a parent of DF.
cd "$(dirname "$0")"

# Find a terminal emulator.
terminal=
for termname in xdg-terminal konsole gnome-terminal xfce4-terminal gtkterm rxvt-unicode rxvt uxterm xterm
do
  if command -v $termname &> /dev/null
  then
    terminal=$termname
    break
  fi
done

if [ -n "$terminal" ]
then
  # Open everything in the background.
  (
    # Launch DFHack in a new terminal window.
    $terminal -e ./df_linux/dfhack "$@" &

    # Open Dwarf Therapist as this PID,
    # so it can read DF memory without root permissions.
    cd therapist/
    exec ./bin/release/DwarfTherapist
  ) &
else
  # Use the tty that was used to launch this script.
  # This causes problems if Therapist is closed first.
  exec 3>&0 4>&1 5>&2
  ./df_linux/dfhack "$@" 0>&3 1>&4 2>&5 &

  # Open Dwarf Therapist as this PID,
  # so it can read DF memory without root permissions.
  cd therapist/
  exec ./bin/release/DwarfTherapist
fi
Advertisements

Written by eswald

26 Mar 2013 at 7:26 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s