4

Here's the thing. I'm running Mint 19, relatively fresh install. I heard a lot of hype about Dwarf Fortress, and installed it once; then, needing to leave in a hurry, closed it with Control-C. Ever since then, every time I attempt to run it, I get the output:

/tmp/dwarf-fortresss7j3cousrun/df: 6: /tmp/dwarf-fortresss7j3cousrun/df: ./libs/Dwarf_Fortress: not found
Traceback (most recent call last):
  File "/usr/games/dwarf-fortress", line 93, in <module>
    main()
  File "/usr/games/dwarf-fortress", line 90, in main
    run_df_in_unionfs_with_cleanup(user_run_dir, data_dirs, sys.argv)
  File "/usr/games/dwarf-fortress", line 60, in run_df_in_unionfs_with_cleanup
    run_df_in_unionfs(user_run_dir, data_dirs, args)
  File "/usr/games/dwarf-fortress", line 54, in run_df_in_unionfs
    run_df(tmp_dir, args)
  File "/usr/games/dwarf-fortress", line 46, in run_df
    subprocess.run(cmd).check_returncode()
  File "/usr/lib/python3.6/subprocess.py", line 369, in check_returncode self.stderr)
subprocess.CalledProcessError: Command '['/tmp/dwarf-fortresss7j3cousrun/df', '/usr/games/dwarf-fortress']'
returned non-zero exit status 127.

Followed by immediate program termination. I have attempted to remove, even purge, and reinstall, dwarf-fortress, only to get the same result. It ran perfectly fine exactly once, and as much as I have been staring at this error, I cannot make sense of it.

It isn't business critical or anything, I'm not technically even a player; but I would really like to know why the program is now failing, and in what manner it was broken by me. It's just too much of a mystery to leave unchecked. Thanks for your time.

4
+50

TL;DR: Use the following script to clean your $XDG_DATA_HOME/$HOME from left-overs and unmount previous unionfs:

#!/bin/sh
set -eu

echo Killing currently running Dwarf Fortress instances
killall -q -9 Dwarf_Fortress || true

echo Removing old Dwarf Fortress unionfs mounts and mount points
find /tmp/ -maxdepth 1 -name "dwarf-fortress*" \
  -printf "  Found %f\n" \
  \( -exec fusermount -u {} \; -o -true \) \
  -exec rmdir {} \;


UNIONFSDIR=${XDG_DATA_HOME:-"${HOME:?}/.local/share/"}dwarf-fortress/run/.unionfs-fuse
if [ -d "$UNIONFSDIR" ]; then
  echo Removing old .unionfs-fuse directory
  rm -r "$UNIONFSDIR"
fi
echo Done. Run dwarf-fortress and praise Armok!

Why does this happen?

The dwarf-fortress package provided by Ubuntu uses a Python wrapper /usr/games/dwarf-fortress. That wrapper creates a secondary data hierarchy in $XDG_DATA_HOME/.local/share/dwarf-fortress/run/.unionfs-fuse, which gets mounted as unionfs(8) together with some other directories.

This enables you to place your mods in your $XDG_DATA_HOME/.local/share/dwarf-fortress directory, so you don't need to change the contents of /usr/share/games/dwarf-fortress, which is great! However, a unionfs must be handled with care and get cleaned up correctly. The Python script failed to do so when you've used C-c to get out of the game.

Therefore, the unionfs is probably still mounted, but in a bad state.

How do I fix it?

So first of all, make sure that the game is completely closed:

killall -s KILL Dwarf_Fortress

Then make sure that there is no leftover unionfs DF mount:

mount | grep -a -e dwarf -e unionfs
unionfs on /tmp/dwarf-fortresswvlaptrarun type fuse.unionfs (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
unionfs on /tmp/dwarf-fortress4ylv2t19run type fuse.unionfs (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)

As you can see, there are currently two on my system. Since both are broken, lets get rid of them with fusermount -u:

fusermount -u /tmp/dwarf-fortresswvlaptrarun 
fusermount -u /tmp/dwarf-fortress4ylv2t19run 

And last, but most important, remove the .unionfs-fuse in $XDG_DATA_HOME/dwarf-fortress/run/. I don't have XDG_DATA_HOME set, so I have to use $HOME. Check env before you accidentally delete the wrong directory!

rm -r $HOME/.local/share/dwarf-fortress/run/.unionfs-fuse

That's it. Note that the df_linux version from Bay 12 Games doesn't run into this problems, as it neither uses a Python wrapper nor a Unionfs.

Overall, I think this issue is worth a bug report, as troubleshooting is a hassle. The dwarf-fortress wrapper should provide a -clean option that handles this case. If possible, please file a bug report on the Debian upstream.

How did you come up with this solution?

First of all, I had a look at file $(which dwarf-fortress), which told me that it's a Python script:

$ file $(which dwarf-fortress)
/usr/games/dwarf-fortress: Python script, ASCII text executable

I then checked the script with any editor and found

def get_user_run_dir():
    old_run_dir = xdg.BaseDirectory.save_data_path('dwarf-fortress')
    new_run_dir = os.path.join(old_run_dir, 'run')

    ...

def run_df_in_unionfs(user_run_dir, data_dirs, args):
    mnt_dirs = user_run_dir + "=rw:" + ':'.join(data_dirs)
    with tempfile.TemporaryDirectory(suffix='run', prefix='dwarf-fortress') as tmp_dir:
        cmd = ['unionfs', '-o', 'cow,relaxed_permissions', mnt_dirs, tmp_dir]
        subprocess.run(cmd).check_returncode()
        try:
            run_df(tmp_dir, args)
        finally:
            subprocess.run(['fusermount', '-u', tmp_dir]).check_returncode()

which showed that there was at least something to be found in xdg.BaseDirectory, which is $HOME/.local/share (unless set otherwise). At the same time, fusermount -u shows that there is an unmount pending after we quit Dwarf Fortress, and mount | grep unionfs confirmed the active mounts in /tmp. I got rid of the whole $XDG_DATA_HOME/dwarf-fortress directory, and it worked again. With strace -ff -e trace=execve dwarf-fortress, I was able to confirm the unionfs mounts and found the .unionfs-fuse directory.

  • I never would have thought of that. It worked perfectly. I wish I could up-vote this twice. – Michael Eric Oberlin Sep 2 '18 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.