I use this code in a multithreaded program:
class A{
public:
FILE *File;
bool ThFunc(){
if (CreateDataFile())
std::cout << "File was created\n";
else{
std::cout << "Can't create file\n";
return false;
}
//work with file
if (CloseDataFile()){
std::cout << "File was closed\n";
return true;
}
else{
std::cout << "Can't close file\n";
return false;
};
};
bool CreateDataFile(){
File = fopen ("binary_file.bin", "wb");
if (File!=NULL)
return true;
else
return false;
};
bool CloseDataFile(){
int t=fclose(File);
if (t!=0)
return false;
else{
//insert the file name in another thread
return true;
}
}
};
Sometimes, in another thread (~every 10 files) there is an error: "The file is used by another program(my.exe)", but in console i see: "File was closed". What could be wrong? Mutex is locked, before use a ThFunc() and unlocked after.
Related
I have a Windows service written in C++ with the Win32 API. Before entering in "service" mode, the C++ program tries to read a configuration file specified as an absolute path. But the program cannot read it and exits. Some debugging leaves me to suspect that this is because of file ownership.
Question is, how can I modify the file ownership (preferably with a power-shell script) , so that the file can be read?
Here are the relevant parts
main program (exits, file cannot be read)
int main()
{
std::string cfg_file_name = config::get_config(config::comm_config_file);
if (cfg.read(cfg_file_name) < 0)
{
events::start_log(cfg.log_path, cfg.log_spdlog_level);
SPDLOG_CRITICAL("Cannot read: " + cfg_file_name);
return 1;
}
function get_config() uses some Win32 API calls to get the executable path (where the file is located) and concatenates it with the file name, to get an absolute path
std::string config::get_config(const std::string& config_name)
{
#ifdef _MSC_VER
std::string s = config::get_executable_path();
//this is done before log starts; it will be written to C:\Windows\System32 as a first log/debugging tool
std::ofstream ofs("comm.txt");
ofs << "GetModuleFileName: " << s << std::endl;
TCHAR buf[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
//change the current directory of the process to be the executable path
if (SetCurrentDirectory(s.c_str()) == 0)
{
ofs << "SetCurrentDirectory" << std::endl;
}
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
s += config_name;
ofs.close();
return s;
#else
return config_name;
#endif
}
service is created by a power-shell script
sc.exe create _comm_ftp_server binPath= "$install_dir\ftp_server.exe" start= auto obj= "NT AUTHORITY\LocalService" password= " "
to debug it, I wrote a simple test service that writes a file and reads that same file, with no problem (so, a file can be read)
int main(int argc, char* argv[])
{
std::string path = config::get_executable_path();
cfg.log_path = path;
events::start_log(cfg.log_path, "trace", true);
//A service process has a SERVICE_TABLE_ENTRY structure for each service that it can start.
//The structure specifies the service name and a pointer to the service main function for that service.
//The main function of a service program calls the StartServiceCtrlDispatcher
//function to connect to the service control manager (SCM)
SERVICE_TABLE_ENTRY service_table[] =
{
{ (LPSTR)service_name, (LPSERVICE_MAIN_FUNCTION)service_main },
{ NULL, NULL }
};
if (StartServiceCtrlDispatcher(service_table))
{
return 0;
}
else
{
return 1;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_main
/////////////////////////////////////////////////////////////////////////////////////////////////////
void WINAPI service_main(DWORD argc, LPTSTR* argv)
{
service_handle = RegisterServiceCtrlHandler(service_name, service_handler);
if (service_handle == NULL)
{
return;
}
service_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (service_stop_event == NULL)
{
return;
}
report_status(SERVICE_START_PENDING);
report_status(SERVICE_RUNNING);
SPDLOG_INFO("service running..." + std::to_string(current_state));
HANDLE thread_service = 0;
thread_service = CreateThread(NULL, 0, service_thread, NULL, 0, NULL);
WaitForSingleObject(thread_service, INFINITE);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service shutdown requested
/////////////////////////////////////////////////////////////////////////////////////////////////////
CloseHandle(thread_service);
report_status(SERVICE_STOP_PENDING);
SPDLOG_INFO("service stop pending..." + std::to_string(current_state));
CloseHandle(service_stop_event);
report_status(SERVICE_STOPPED);
SPDLOG_INFO("service stopped..." + std::to_string(current_state));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_thread
/////////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI service_thread(LPVOID lpParam)
{
std::string path = cfg.log_path;
SPDLOG_INFO("service started in..." + cfg.log_path);
path += "\\test.txt";
size_t i = 0;
while (WaitForSingleObject(service_stop_event, 0) != WAIT_OBJECT_0)
{
write_txt_file(path, "writing...#" + std::to_string(i));
i++;
Sleep(10000);
read_txt_file(path);
}
return ERROR_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//write_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void write_txt_file(const std::string& file_name, const std::string& input)
{
FILE* f = fopen(file_name.c_str(), "a+");
fprintf(f, "%s\n", input.c_str());
fclose(f);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//read_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void read_txt_file(const std::string& file_name)
{
std::ifstream ifs;
ifs.open(file_name);
if (!ifs.is_open())
{
SPDLOG_ERROR("Cannot open: " + file_name);
return;
}
std::string line;
while (std::getline(ifs, line))
{
SPDLOG_INFO("Line: " + line);
}
ifs.close();
}
Examining the file written by the test service in Windows explorer (Properties->Details) reveals a file owner as LOCAL_SERVICE
The file that must be read has owner "Administrators"
This leaves me to suspect that this is the problem. How can the file ownership be changed, or is there a way to create the service with privileges that can read any file ?
reference for SC.EXE Create
https://learn.microsoft.com/en-US/windows-server/administration/windows-commands/sc-create
To read the file, std::ifstream is used (default read only)
int config::config_t::read(const std::string& fname)
{
try
{
std::ifstream ifs(fname);
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
}
catch (const std::exception& e)
{
SPDLOG_ERROR(e.what());
return -1;
}
return 0;
}
The read error was because the library JSON for modern C++
https://github.com/nlohmann/json
detects an error reading the last entry of this file because of the comma ","
{
"archive_path":"D:\\archive",
"test_comm_input_path":"D:\\test_comm_input_path",
}
in the reading function
int config::config_t::read(const std::string& fname)
{
//this is done before log starts; it will be written to the executable path as a first log/debugging tool in service mode
std::ofstream ofs("comm.txt");
try
{
std::ifstream ifs;
std::ios_base::iostate mask = ifs.exceptions() | std::ios::failbit;
ifs.exceptions(mask);
ifs.open(fname);
if (!ifs.is_open())
{
ofs << "open fail: " << fname << std::endl;
ofs.close();
return -1;
}
else
{
ofs << "open: " << fname << std::endl;
}
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
ofs << "read: " << fname << std::endl;
ofs.close();
}
catch (const std::exception& e)
{
ofs << "json read: " << e.what() << std::endl;
ofs.close();
return -1;
}
return 0;
}
where
void from_json(const nlohmann::json& j, config::config_t& c)
{
if (j.contains("archive_path"))
{
j.at("archive_path").get_to(c.archive_path);
}
}
I have 2 functions that the main run them as a thread. first one reads from a file of messages and pushes every line into a queue and the second function writes into a file the messages of the queue.
from some reason, the queue in the second function doesn't update and the function writes nothing to the output file
here is the first func:
void MessagesSender::saveMessages()
{
std::string line = "";
std::ifstream file;
file.open(DATA_FILE);
if (file.is_open())
{
while (getline(file, line))
{
m.lock();
messages.push(line);
m.unlock();
}
file.close();
file.open(DATA_FILE, std::ofstream::out | std::ofstream::trunc);//clear file
file.close();
}
}
Here is the second:
void MessagesSender::sendMessages()
{
std::ofstream file(OUTPUT_FILE);
m.lock();
std::queue<std::string> temp_messages = messages;//problem - "messages" is empty
m.unlock();
if (file.is_open())
{
int i = 0;
while (!temp_messages.empty())
{
while (i < userName.size())
{
file << userName[i] << ": " << temp_messages.front() << std::endl;
i++;
}
temp_messages.pop();
i = 0;
}
file.close();
}
else std::cout << "Unable to open file";
}
int main()
{
MessagesSender mailingList;
while (mailingList.menu()){}//shows to the user the menu
std::thread t1(&MessagesSender::saveMessages, mailingList);
std::thread t2(&MessagesSender::sendMessages, mailingList);
t1.join();
t2.join();
}
why "messages" is empty and how can I fixe it?
I need to purposely fail the "fopen"-ing of a file to test my code. How do I do that. My code so far:
void MeshTrian::init (std::string filename)
{
if (!sofa::helper::system::DataRepository.findFile(filename))
{
msg_error() << "File '" << filename << "' not found." ;
return;
}
FILE *f = fopen(filename.c_str(), "r");
bool status;
msg_error_when(!(status=f))<<sofa::helper::message::UnableToOpenFile(filename.c_str());
if (status)
{
readTrian (f);
fclose(f);
}
}
FileLocker_wo.h
#include <string>
namespace Utils
{
namespace FileLocker
{
bool lock_file(std::string aFileName, int& aFileDescriptor);
bool unlock_file(int& aFileDescriptor);
bool is_file_locked(std::string aFileName);
};
}
FileLocker_wo.cpp
namespace Utils
{
namespace FileLocker
{
bool lock_file(std::string aFileName, int& aFileDescriptor)
{
aFileDescriptor = open(aFileName.c_str(), O_RDWR);
if (aFileDescriptor != -1)
{
if (lockf(aFileDescriptor, F_TLOCK, 0) == 0)
{
return true;
}
std::cout << strerror(errno) << std::endl;
}
return false;
}
bool unlock_file(int& aFileDescriptor)
{
if (lockf(aFileDescriptor, F_ULOCK, 0) == 0)
{
std::cout << "unloced file" << std::endl;
close(aFileDescriptor);
return true;
}
close(aFileDescriptor);
return false;
}
bool is_file_locked(std::string aFileName)
{
int file_descriptor = open(aFileName.c_str(), O_RDWR);
if (file_descriptor != -1)
{
int ret = lockf(file_descriptor, F_TEST, 0);
if (ret == -1 && (errno == EACCES || errno == EAGAIN))
{
std::cout << "locked by another process" << std::endl;
close(file_descriptor);
return true;
}
if (ret != 0)
{
std::cout << "return value is " << ret << " " << strerror(errno) << std::endl;
}
}
close(file_descriptor);
return false;
}
}
}
p1.cpp
#include <iostream>
#include <fstream>
#include "FileLocker_wo.h"
int main()
{
int fd = -1;
if (Utils::FileLocker::lock_file("hello.txt", fd))
{
std::ofstream out("hello.txt");
out << "hello ding dong" << std::endl;
out.close();
std::cout << "locked" << std::endl;
sleep(5);
if (Utils::FileLocker::unlock_file(fd))
{
std::cout << "unlocked" << std::endl;
}
}
return 0;
}
p2.cpp
#include "FileLocker_wo.h"
#include <iostream>
#include <fstream>
int main()
{
int max_trys = 2;
int trys = 0;
bool is_locked = false;
do
{
is_locked = Utils::FileLocker::is_file_locked("hello.txt");
if (!is_locked)
{
std::cout << "not locked" << std::endl;
break;
}
std::cout << "locked" << std::endl;
sleep(1);
++trys;
}
while(trys < max_trys);
if (!is_locked)
{
std::string s;
std::ifstream in("hello.txt");
while(getline(in,s))
{
std::cout << "s is " << s << std::endl;
}
}
return 0;
}
I am trying to get a file lock in one process and checking whether there is any lock on that file in other process using lockf (p1.cpp, p2.cpp).
In p1.cpp I am locking the file hello.txt and waiting for 5 seconds. Meanwhile I start p2.cpp and checking whether any lock is there by other process, but always getting there is no lock> I am stuck with this for last 2 hours.
Can anybody tell what is wrong in this?
You've tripped over one of the nastier design errors in POSIX file locks. You probably didn't know about this because you only read the lockf manpage, not the fcntl manpage, so here's the important bit of the fcntl manpage:
If a process closes any file descriptor referring to a file, then
all of the process's locks on that file are released, regardless of
the file descriptor(s) on which the locks were obtained.
What this means is, in this bit of your code
if (Utils::FileLocker::lock_file("hello.txt", fd))
{
std::ofstream out("hello.txt");
out << "hello ding dong" << std::endl;
out.close();
you lose your lock on the file when you call out.close(), even though out is a different OS-level "open file description" than you used in lock_file!
In order to use POSIX locks safely you must ensure that you call open() on the file to be locked once and only once per process, you must never duplicate the file descriptor, and you must only close it again when you are ready to drop the lock. Because there may not be any way (even using unportable extensions) to construct an iostreams object from a file descriptor, or to extract a file descriptor from an iostreams object, the path of least resistance is to use only OS-level I/O primitives (open, close, read, write, fcntl, lseek, ftruncate) with files that you need to apply POSIX locks to.
I convert QFile into FILE* to use some third-part libraries. Here is code:
QTemporaryFile pack200_file;
//Here write something into pack200_file
......
pack200_file.seek(0);
int handle_in = pack200_file.handle();
if (handle_in == -1)
{
qCritical() << "Error reopening " << pack200_file.fileName();
return false;
}
FILE * file_in = fdopen(handle_in, "r");
if(!file_in)
{
qCritical() << "Error reopening " << pack200_file.fileName();
return false;
}
QTemporaryFile qfile_out;
if(!qfile_out.open())
{
qCritical() << "Error opening " << qfile_out.fileName();
return false;
}
int handle_out = qfile_out.handle();
if (handle_out == -1)
{
qCritical() << "Error opening " << qfile_out.fileName();
return false;
}
FILE * file_out = fdopen(handle_out, "w");
if (!file_out)
{
qCritical() << "Error opening " << qfile_out.fileName();
return false;
}
try
{
unpack_200(file_in, file_out);
}
catch (std::runtime_error &err)
{
qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
return false;
}
//success
QString finalJarname = .....;
QFile::remove(finalJarname);
QFile::copy(qfile_out.fileName(), finalJarname);
fclose(file_in);
fclose(file_out);
qfile_out.remove(); //Here I got crash
pack200_file.remove();
return true;
I got crash at the line qfile_out.remove();, It seems the remove operation cause it. But I got nothing from trace stack and visual studio do not mention me which code trigger the crash finally.
If I change the code into:
fclose(file_in);
fclose(file_out);
qfile_out.setAutoRemove(false);
pack200_file.setAutoRemove(false);
qfile_out.close();
pack200_file.close();
return true;
it will also crash when return ;
Then I change IDE into QtCreator, it said:
Second Chance Assertion Failed: File
f:\dd\vctools\crt\crtw32\lowio\close.c , Line 47
Expression: (_osfile(fh) & FOPEN)
But I can't find the file f:\dd\vctools\crt\crtw32\lowio\close.c.
How can I localize the source of the crash?
You closed qfile_out's file for it with fclose(). Looks like the Visual C runtime library didn't like that, hence the exception. Suggest you remove the calls to fclose... or avoid mixing Qt and non-Qt file operations.