Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
174 views
in Technique[技术] by (71.8m points)

Linux File Locking with fcntl and C++

I did two months search on the web for a proper file locking mechanism to be used in a C++ program.

I found a lot on "C and fnctl" which I could proof to work. But all really proper working locking mechanism, that I could proof to work in Linux are only based on file descriptors.

As this seems to be something really old fashined and in actual C++17 style of writing C++ code with file- and ip-streams not using that mechanism, I only came up with something that works with using what was presented here:

Not able to ofstream using __gnu_cxx::stdio_filebuf

My Question is, is this really the only mechanism working? To connect both worlds?

I looked in all these books to find anything about fcntl and C++, but was not successful:

My question to the C++ gurus here is, if I missed something, or if the following code is, today, begin of 2021 the best we could do.

Short explanation of what the code is a proof for:

We have a C++ Code which adds usernames and its LSF-processes to a conf-file, which is read by SSH-server to allow user access to that machine. As at the same time two or more running processes of this code could lead to concurrent attempts of adding or deleting users from this file could occur, we have to proof that proper file locking is preventing that. Without using an extra "access" file, which also could be a solution.

This is some example code I tested:


#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <fcntl.h>
#include <unistd.h>
#include <ext/stdio_filebuf.h>

using namespace std::this_thread;  // for sleep_for

int main( ) {
  // set unbuffered concole output
  std::cout.setf(std::ios::unitbuf);

  const char* filename {"testfile.txt"};

  // get input from input_from_user

  std::string input_from_user_string;
  std::cout << "Please give input to change in the file: ";
  std::cin >> input_from_user_string;

  int add_1_del_2 = 0;
  std::cout << "Please give 1 if you want to add to the file or 2 if you want to delete from file: ";
  std::cin >> add_1_del_2;

  int input_from_user_time;
  std::cout << "Please give seconds to wait: ";
  std::cin >> input_from_user_time;


  // opening file

  std::cout << "Opening File" << std::endl;

  mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; //664

  int fd;
  fd = open(filename, O_RDWR | O_CREAT, mode);

  // printing out information about file descriptor
  std::cout << " Dexc:" << fd << std::endl;

  // generating C++-streams on filedescriptor
  __gnu_cxx::stdio_filebuf<char> sourcebufin(fd, std::ios::in);
  __gnu_cxx::stdio_filebuf<char> sourcebufout(fd, std::ios::out);
  std::istream myfilein(&sourcebufin);
  std::ostream myfileout(&sourcebufout);


  // -----------
  // check for file Locking or exit
  // -----------

  // creating structure for file locking
  struct flock fl;
  fl.l_type = F_RDLCK;
  fl.l_whence = SEEK_SET;
  fl.l_start = 0;
  fl.l_len = 0;

  // set file locking for read
  fl.l_type = F_RDLCK;


  std::cout << "Checking for Lock on file" << std::endl;

  // check for file locking on file for read only once

  (void) fcntl(fd, F_GETLK, &fl);

  if (fl.l_type != F_UNLCK) {
    std::cout << "File is locked for reading by process "
         << fl.l_pid
         << ", in status"
         << ((fl.l_type == F_WRLCK) ? 'W' : 'R')
         << ", start="
         << fl.l_start
         << ", end="
         << fl.l_len
         << std::endl;
  }
  else {
    (void) printf("File is unlocked for reading
");
  }

  // set file locking for write
  fl.l_type = F_WRLCK;

  // check for file locking on file for write in a loop
  for (int i = 1; i < 11; i++) {
    //printf("Checking for lock %d of 10 times...
", i);
    std::cout << "Checking for lock "
         << i
         << " of 10 times..."
         << std::endl;
    (void) fcntl(fd, F_GETLK, &fl);

    if (fl.l_type != F_UNLCK) {
      //(void) printf("File is locked by process %d, in status %c, start=%8ld, end=%8ld
", fl.l_pid,
      //      , fl.l_start, fl.l_len);
      std::cout << "File is locked by process "
           << fl.l_pid
           << ", in status"
           << ((fl.l_type == F_WRLCK) ? 'W' : 'R')
           << ", start="
           << fl.l_start
           << ", end="
           << fl.l_len
           << std::endl;
      sleep(10);
    }
    else {
      (void) printf("File is unlocked
");
      break;
    }
  }



  // -----------
  // apply lock for write on file
  // -----------

  // locking file

  std::cout << "Locking file for write" << std::endl;

  // set file locking for write again, as checking on lock resets it
  fl.l_type = F_WRLCK;

  if (fcntl(fd, F_SETLKW, &fl) == -1) {
    perror("fcntl");
    abort();
  }



  // -----------
  // wait some time
  // -----------

  std::cout << "Now waiting for " << input_from_user_time << " seconds, keeping the file locked..." << std::endl;
  std::this_thread::sleep_for(std::chrono::seconds(input_from_user_time));



  // -----------
  // read from file
  // -----------

  std::cout << "Reading from file... " << std::endl;
  myfilein.seekg(0, std::ios::end);
  size_t size_before = myfilein.tellg();
  myfilein.seekg(0);

  std::string filecontent{""};
  filecontent.reserve(size_before);

  std::cout << "Length of file is: " << size_before << std::endl;

  // read full content of file in string "filecontent"
  filecontent.assign((std::istreambuf_iterator<char>(myfilein)),
            std::istreambuf_iterator<char>());



  // -----------
  // print output about read data
  // -----------

  std::cout << "Length of filecontent-string: " << filecontent.size() << std::endl;

  std::cout << "Content of File begin" << std::endl;
  std::cout << "----------" << std::endl;
  std::cout << filecontent << std::endl;
  std::cout << "----------" << std::endl;



  // -----------
  // Apply changes on read in data depending on given input
  // -----------

  if (add_1_del_2 == 2) {
    std::cout << "Runmode: Del" << std::endl;
    std::string string_to_delete = input_from_user_string+"
";
    std::string::size_type pos_of_found_substring = filecontent.find(string_to_delete);

    if (pos_of_found_substring != std::string::npos) {
      filecontent.erase(pos_of_found_substring, string_to_delete.length());
    }
    else {

    }

  }

  if (add_1_del_2 == 1) {
    std::cout << "Runmode: Append" << std::endl;
    filecontent.append(input_from_user_string);
  }

  std::cout << "Content of String after change" << std::endl;
  std::cout << "----------" << std::endl;
  std::cout << filecontent << std::endl;
  std::cout << "----------" << std::endl;



  // -----------
  // write out to file, truncate before to length of new string
  // -----------

  std::cout << "Now starting the write out..." << std::endl;
  myfilein.seekg(0);

  ftruncate(fd,filecontent.length());

  myfileout.seekp(0);
  myfileout << filecontent;
  myfileout.flush();
  myfileout.clear();



  // -----------
  // read from file for a second time and printout content
  // -----------

  std::cout << "Reading from file again... " << std::endl;
  myfilein.seekg(0, std::ios::end);
  size_t size_after = myfilein.tellg();
  myfilein.seekg(0);

  std::string filecontent_after{""};
  filecontent_after.reserve(size_after);

  std::cout << "Length of file is now: " << size_after << std::endl;

  // read full content of file in string "filecontent"
  filecontent_after.assign((std::istreambuf_iterator<char>(myfilein)),
            std::istreambuf_iterator<char>());

  std::cout << "Length of filecontent_after-string: " << filecontent_after.size() << std::endl;

  std::cout << "Content of File end" << std::endl;
  std::cout << "----------" << std::endl;
  std::cout << filecontent_after << std::endl;
  std::cout << "----------" << std::endl;



  // -----------
  // unlocking file and close file
  // -----------

  printf("Unlocking...
");

  fl.l_type = F_UNLCK;
  if (fcntl(fd, F_SETLK, &fl) == -1) {
    perror("fcntl");
    abort();
  }

  close(fd);



  // -----------
  // done
  // -----------

  std::cout << "done" << std::endl;

  exit(0);
}

I ask for your comments on this or perhaps how to improve.

Alexander Bruns

question from:https://stackoverflow.com/questions/65861873/linux-file-locking-with-fcntl-and-c

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...