6

I have a program reading from a file "foo" using C++ using:

pFile = fopen ("foo" , "r");

I want it to stop executing the rest of the function if the file is a named pipe. Is there a way to check if the file is a named pipe before opening it?

I found the exact same question using python: Check if file is a named pipe (fifo) in python? Can I do something similar in C++?

3 Answers 3

9

From man 2 stat:

int fstat(int filedes, struct stat *buf);

...The following POSIX macros are defined to check the file type using the st_mode field:

         S_ISFIFO(m) FIFO (named pipe)?

So struct stat st; ... !fstat(fileno(pFile, &st) && S_ISFIFO(st.st_mode) should work.

Edit: See also SzG's excellent answer, and Brian's comment to it.

1
  • 1
    There is also S_ISFIFO(st.st_mode), which is a macro that tests for a pipe or FIFO special file. It is not clear that you can distinguish a pipe from a FIFO. Commented Jan 30, 2014 at 22:51
2

It might be a bit too late to stop the execution AFTER fopen(). It's because the open() syscall will block until someone has opened the FIFO for writing. Instead, use the stat() syscall (on Unix/Linux) before fopen() to find out.

2
  • 6
    Using stat then fopen can lead to a TOCTTOU vulnerability. The best solution if this is a security issue is to open non-blocking then fstat and fdopen.
    – Brian Bi
    Commented Jan 30, 2014 at 22:42
  • 2
    I thought a non-blocking open() means that the subsequent read() calls are non-blocking. But looking up the man pages confirmed that the open() itself becomes non-blocking too.
    – SzG
    Commented Jan 30, 2014 at 22:54
0

In modern C++ there is also filesystem library, it is available since C++17 #include <experimental/filesystem>, in C++14 it was experimental #include <experimental/filesystem>

So you can you use is_fifo() now. filesystem::path class is constructible from std::string, which is construcible from const char*, so filesystem::is_fifo("/path/to/file") will work as expected. But this version can throw an exception, so bool is_fifo( const std::filesystem::path& p, std::error_code& ec ) noexcept; is your choice.

#if __cplusplus >= 201703L
  #include <filesystem>
  namespace filesystem = std::filesystem;
#else
  #include <experimental/filesystem>
  namespace filesystem = std::experimental::filesystem;
#endif

bool is_fifo(const char *path)
{
    std::error_code ec;
    bool res = filesystem::is_fifo(path, ec);
    if (ec.value() != 0)
        std::cerr << ec.message() << std::endl;
    return res;
}

And don't forget this notice:

GNU implementation prior to 9.1 requires linking with -lstdc++fs and LLVM implementation prior to LLVM 9.0 requires linking with -lc++fs.

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