Named pipes and unwanted hangs

The past few days I’ve been dealing with a wonderful little problem.

They’re called “named pipes“.

The main product I work on in my day job was doing some filesystem scans and would hang for some unknown reason. After much investigation we noticed the file it was hanging on was a named pipe file way down in the bowels of the system (a temporary file created by the com.apple.notify process). What was happening was well… it’s a pipe. Something in my app was opening that pipe and thus the app “hung” because it was now wanting to read from the pipe. Thing is, my app doesn’t care about pipes at all, it’s just working with files. As well, we weren’t explicitly opening the file; we would call some other OS routine and somehow somewhere in that OS function’s implementation it called open() and thus we hung.

And so, what a bear this is.

In the end we decided to check for and avoid pipe and socket files at all costs. Any means by which a file can “get into” the app, we put up a wall at the perimeter so no such files can even get in. We figure we keep them out at the wall, then we don’t have to spend lots of CPU cycles internally to constantly deal with them (tho critical code should still perform checks). Plus, since one big part of the problem is we can’t control what others do and if they might open() the file, we have to nip it in the bud as soon we become aware of the file and minimize how much “work” is done on the file in case some other function might open() the file and we risk hanging.

To check is pretty simple. Code something like this (NB: I tried using the “code” tags and less-than/greater-than signs for the #include, but WordPress’ editor seems to get confused… so I just used the “pre” tag, which isn’t quite giving me the results I want either… oh well.).

#include "sys/stat.h"

bool
FileUtilities::IsUnsupportedFilesystemItem(
const char*    inPath)
{
    bool is = true; // assume failure, because if we fail on such 
                    // a simple operation something really has to be wrong
    if (inPath != NULL)
    {
        struct stat statInfo;
        int retval = lstat(inPath, &statInfo); // use lstat() instead of stat() because 
                                                   // we want to minimize resolutions or other "work"
        if (retval == 0)
        {
            if (!S_ISFIFO(statInfo.st_mode) && !S_ISSOCK(stat.st_mode))
            {
                is = false;
            }
        }
    }
    return is;
}

How you actually cope with it or if you consider them problems or not is up to you and your code and your situation. The key of the above is to get the file’s mode then check the FIFO and SOCK flags. If set, reject the file.

For most people, this isn’t going to be an issue or a problem. I mean, we went around for quite some time and never dealt with the issue. And in daily use, most people aren’t going to see pipe or socket files.

But it’s worth noting and thinking about. Nothing wrong with being a little defensive.