Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C++, how to detect that file has been already opened by own process?

Tags:

c++

file

I need to create a logger facility that outputs from different places of code to the same or different files depending on what the user provides. It should recreate a file for logging if it is not opened. But it must append to an already opened file.

This naive way such as

std::ofstream f1(“log”);
f1 << "1 from f1\n";
std::ofstream f2(“log”);
f2 << "1 from f2\n";
f1 << "2 from f1\n";

steals stream and recreates the file. Log contains

1 from f2

With append, it will reuse file, but the second open steals stream from f1. Log contains

1 from f1
1 from f2 

Try to guess which files will be used and open them all in the very begging will work but may create a lot of files that are not actually used.

Open for append and closing on each logging call would be an almost working solution, but it seems to be a slow solution due to a lot of system calls and flushing on each logging action.

I’m going to create a static table of opened files, hoping that std::filesystem::canonical will work in all of my cases. But as far as I understand such a table should already exist somewhere in the process.

I've read that in Fortran people can check if a file was opened using inquire.

Check whether file has been opened already

But that answer did not give me any insight on how to achieve the same with С/C++.


Update

A scratch of the logger with a "static" table of open logs can look like

//hpp
class Logger {
  static std::mutex _mutex;
  static std::unordered_map<std::string, std::ofstream> _openFiles;
  std::ostream& _appender;
  std::ostream& _createAppender(const std::filesystem::path& logPath);
public:
  Logger(const std::filesystem::path& logPath):
    _appender(_createAppender(logPath)) {
  }
  template<class... Args>
  void log(const Args&... args) const {
    std::scoped_lock<std::mutex> lock(_mutex);
    (_appender << ... << args);
  }
};
//cpp
#include "Logger.hpp"

std::mutex Logger::_mutex;
std::unordered_map<std::string, std::ofstream> Logger::_openFiles;

std::ostream& Logger::_createAppender(const std::filesystem::path& logPath) {
  if (logPath.empty()) return std::cout;
  const auto truePath{std::filesystem::weakly_canonical(logPath).string()};
  std::scoped_lock<std::mutex> lock(_mutex);
  const auto entry{_openFiles.find(truePath)};
  if (entry != _openFiles.end()) return entry->second;
  _openFiles.emplace(truePath, logPath);
  std::ostream& stream{_openFiles[truePath]};
  stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
  return stream;
}

maybe it will help someone.

Yet, I still wonder if it is possible to get table mapping handles/descriptors from OS mentioned by @yzt, and will accept as an answer if someone explains how to do that inside the program.

like image 403
Askold Ilvento Avatar asked Dec 07 '21 08:12

Askold Ilvento


People also ask

How do you check if a file has been opened in C?

Check to make sure the file was successfully opened by checking to see if the variable == NULL. If it does, an error has occured. Use the fprintf or fscanf functions to write/read from the file. Usually these function calls are placed in a loop.

How do you check if a file is being used by another process in Windows C#?

Solution 1 First check if the file exists (File. Exists) if so try to open for write within try and catch block, if exception is generated then it is used by another process.

How do you get the files that are opened by an application?

To see the open files for a process, select a process from the list, select the View->Lower Panel View->Handles menu option. All of the handles of type "File" are the open files. Also, a great way to find which application has a file open is by using the Find->Handle or DLL menu option.

How to check if a file is already open?

As a side note: If you wanted to check if the file is already open (by any other process or thread), you can try getting a file write lease (fcntl(fileno(stream),F_SETLEASE,F_WRLCK)). It will fail if the file is already open by anyone else. This only works for normal files owned by the user.

How to find all files currently opened by the cron process?

Here is an example that finds all files currently opened by the cron process. The PID for cron is 323762, which is 0x4F0B2 in hex. We can use the slot number to display the file system state info and the file descriptor table. In this example we see that cron has 13 opened files, numbered from 0 to 12.

Why does a process create files but not close them?

A process might have a defect that causes it to continuously create files without closing them, or it might open files, read and write to those files, but fail to close the files afterwards.

How to find all files opened by a process in Unix?

We can use find command to find all file names in the filesystem /usr with an inode of 13407. Notice the 1 just before the first bin. This indicates that there is only 1 hard link, meaning the file name hostmibd.cat is the only file name associated with this inode. The AIX pstat command can be used to list all files opened by a process.


Video Answer


1 Answers

So here is a simple Linux specific code that checks whether a specified target file is open by the current process (using --std=c++17 for dir listing but any way can be used of course).

#include <string>
#include <iostream>
#include <filesystem>

#include <sys/types.h>
#include <unistd.h>
#include <limits.h>

bool is_open_by_me(const std::string &target)
{
    char readlinkpath[PATH_MAX];

    std::string path = "/proc/" + std::to_string(getpid()) + "/fd";
    for (const auto & entry : std::filesystem::directory_iterator(path)) {
        readlink(entry.path().c_str(), readlinkpath, sizeof(readlinkpath));
        if (target == readlinkpath)
            return true;
    }

    return false;
}

Simply list the current pid's open handles via proc, then use readlink function to resolve it to the actual file name.

That is the best way to do it from the userspace I know. This information is not known by the process itself, it is known by the kernel about the process, hence the process has to use various tricks, in this case parsing procfs, to access it.

If you want to check whether a different process hold an open handle to a file, you will have to parse all the procfs for all processes. That may not be always possible since other processes may be run by different users.

All that said - in your specific case, when you are the one owner, opening and closing the files - maintaining a table of open handles is a much cleaner solution.

like image 141
Yuri Nudelman Avatar answered Nov 15 '22 10:11

Yuri Nudelman