Monthly Archives: June 2014

Unix File System Hierarchy for Game Designers

This is a set of recommendations for Unix game designers, based on FHS 2.3, which is the current Unix Filesystem Hierarchy Standard.

The FHS is a set of recommendations about where particular types of files are located on a Unix (or Linux) computer system. Obeying the recommendations of the FHS makes your game easy for people to package in standard Unix installers and prevents it from being held out of linux distributions for putting things where they don’t belong.

Also, restrictions and permissions about what users are allowed to do and where they are permitted to write files are based on the assumption that software follows the FHS rules.

Finally, in some cases local networks will be configured in such a way that parts of directory structures are shared, and this is also facilitated by the FHS. Areas reserved for architecture independent binary data, for example, may be symlinked to the same directory from dozens of machines with different architectures – so if your program as compiled for an AMD64 machine writes a file in there that has AMD64 code in it, and then your program as compiled for an IA64 processor, running on another machine in the network, tries to load and run it, your failure to follow the FHS will be the cause of the crash that results.

So if your game does something that’s against the FHS rules, there will likely be a lot of places where systems run security rules that don’t permit it to be installed, or where there is no write access to files you expect to be able to write, or where you doing something you assume you can do will result in crashes because of data shared between machines that aren’t binary-compatible.

For those reasons, and because you’re a nice person, it’s worth your effort to follow the rules if you’re developing a game for Unix or Linux.

There are three installation scenarios we care about in installing games: User-specific, local, and standard.

User-specific installation is installation of a game by someone who doesn’t have root privileges on the machine. This puts all files in that user’s home directory and owned by that user. If the user’s account (and home directory) is deleted, the game is therefore completely uninstalled. The FHS imposes no internal structure on a user-specific installation; a game developer may structure it however he or she likes.

The usual way of doing this is to put everything inside a directory in the user’s home directory. And the usual way of naming that directory is with a name that is the name of the application prepended by a period. For example, if the user’s home directory is /home/bear/ and your game is named ‘Gargoyle’, the top directory that you install everything in should be named /home/bear/.gargoyle/ .

If you support user-specific installation, then your installer should do it if it detects that it is unable to get the privileges to write in the directories it would use for a standard installation.

Local installation is installation of a game by hand, by the local site administrator. Generally it is used when a standard installation has been done but the site admin wants the local machine (or the local network) to run a version of the game different from the standard installation. This can be because the game had to be changed to fit the local OS conventions or local censorship policy, or because they don’t want a tournament to be interrupted by an automated package update, or because they are actively modifying the game and don’t want their experimentation to affect the standard installation, etc. A local installation installs its files where the system will find them before it finds the files of the standard installation.

Your game installer should never do a local installation by default: ‘local’ directories are reserved specifically for the use of the site administrators. You may include an automated way to make a local installation, but it must never be called other than by the explicit and intentional command of a local site administrator. In particular, ordinary package installations, upgrades, downgrades, or removals must not affect a local installation.

A standard installation is a game installed in a standard way – typically by root in the course of updating or installing a standard package, without any special effort. This isn’t part of the FHS specifically, but the binaries should not be owned by ‘root’. Usually any executable file should be owned by the user ‘games’. If it requires write access anywhere outside the player’s home directory, it should be marked suid and the files it needs to be able to write should also be marked as owned by ‘games’.

I’m going to talk about a game named ‘gargoyle’ as an example. Whenever you see that word in one of the directory or filenames mentioned below, make an appropriate substitution for your game.

Where the FHS specifies a directory used only by a single game, it doesn’t specify the layout or filenames within the directory. Thus, whenever this document talks about a set of files being in a particular directory, you’re allowed to name them anything and lay them out however you like within that directory.

The game binary:
Is the file /usr/games/gargoyle in a standard installation.
Is the file /usr/local/games/gargoyle in a local installation.

The game man page:
Is the file /usr/share/man/man6/gargoyle.6 in a standard installation (note, the filename may be gargoyle.6.gz rather than gargoyle.6
depending on man configuration and page storage format).

is the file /usr/local/man/man6/gargoyle.6 in a local installation
(note, the filename may be gargoyle.6.gz rather than gargoyle.6, as
above).

note: /usr/local/share/man/ is the same directory as /usr/local/man
(one or the other will be a symlink, but which one may vary from system to
system).

Files of architecture-independent static game data: This is data that does not change as a result of playing the game. It specifically *DOES NOT* include high score tables, savefiles, logs, etc. If your game reads script files that determine game behavior or probabilities, etc, when initializing, those script files are included. If your game displays help text that’s stored in files, the files belong here.

Note that /usr/share (and /usr/local/share) and its subdirectories including the static game data and man pages may be on a drive mounted by several different machines of different architectures which all run the same version of the OS and “share” the installation data. If you have data that makes sense on an i386 machine and doesn’t make sense on a SPARC machine, don’t put it here.

Architecture independent static game data:
Goes in a directory named /usr/share/games/gargoyle in a standard installation.

Goes in a directory named /usr/local/share/games/gargoyle in a local installation.

Source code: Note that most standard game installers do not include source code – usually it is packaged separately in another installer, and sometimes it is not provided by the developer. But if you provide it, then it goes here.

Goes in a directory named /usr/src/gargoyle in a standard installation.
Goes in a directory named /usr/local/src/gargoyle in a local installation.

Modifiable files that do change as a result of playing the game, including high score tables, game play logs, shared savefiles, etc …

Go in a directory named /var/games/gargoyle in a standard installation.
Go in a directory named /var/local/games/gargoyle in a local installation.

Modifiable files that do change as a result of playing the game, if they are owned by a particular user and not visible to any other user of the game, such as game play logs, non-shared savefiles, etc, can be stored in the above location or treated as per-player configuration files (see below).

Configuration files: Site-wide “default” configuration file, if singular, is the file /etc/gargoyle in both standard and local installations.

Site-wide “default” configuration files, if there is more than one file, all go in a directory named /etc/gargoyle in both standard and local installations.

A per-player config file, if there’s only one per player, is the file
.gargoyle in the user’s home directory.

If there is more than one per-player config file, then all of them go
into a directory named ‘.gargoyle’ in each player’s home directory.

If there is user-specific configuration for a game, it belongs in the user’s home directory. You either put all the user-specific configuration the game requires in one file whose name starts with a period, or you put all of the files under one directory whose name starts with a period.

Per-player configuration files if any should be owned by the player’s account, not by the user ‘games.’ ‘Games’ exists only so that players of the game may share data without making potentially dangerous use of the ‘root’ account.

Note that per-player configuration files should not be created by the installer. They should only be created when the player, having actually invoked the game, makes a configuration change or save that differs from the site-wide or default configuration. If non-players, or players whose configuration is identical to the default or site-wide configuration, have their own configuration files, it wastes disk space.

Note that users’ home directories are usually in /home/$username, but the setup is site-specific, and some users (usually root) don’t use /home anyway, so don’t rely on the directory named /home.

Use getenv(“HOME”) or getpwuid(geteuid())->pw_dir or whatever equivalent you have on your system or in the libraries of your preferred language to find the user’s home directory. 999 times out of a thousand it’ll be in /home, but you never know.