I'm trying to use the DirectoryWatcher class from POCO's file system library to monitor a specific folder for changes. The code is pretty simple and looks like this:
Monitor::Monitor() {
pattern = new Glob("/path/to/dir/*.dat",
Glob::GLOB_DOT_SPECIAL);
watcher = new DirectoryWatcher(std::string("/path/to/dir"));
watcher->itemAdded += delegate(this, &Monitor::onFileAdded);
watcher->itemModified += delegate(this, &Monitor::onFileChanged);
}
void Monitor::onFileAdded(const DirectoryWatcher::DirectoryEvent& addEvent) {
if (pattern->match(addEvent.item.path())) {
std::cout << "File added: " << addEvent.item.path() << std::endl;
}
}
void Monitor::onFileChanged(const DirectoryWatcher::DirectoryEvent& changeEvent) {
if (pattern->match(changeEvent.item.path())) {
std::cout << "File changed: " << changeEvent.item.path() << std::endl;
}
}
I'm observing some odd behavior. If I copy a new version of a file over a file that's already in the watched folder, I receive the 'item changed' notification twice. If I open a file that's already in the watched folder, edit its contents and save it, I receive an 'item added' notification followed by an 'item changed' notification.
This is on Ubuntu Linux 14.04 and ext4 file system with POCO 1.4.6p2.
Has anyone else observed similar behavior? Could this be related to some specific characteristic of my machine and the OS/file system combo? Is it possible to filter the unwanted events somehow?
Thanks in advance.
Related
I'm setting up a micro-service for a cpp application, and i need to send a post request with a file as a parameter from a js project to the cpp one through this micro-service, i've been able to send the post request but i can't retrieve the file.
I've tried to use "http_response.extract-string/json" but it doesn't work, the result is empty
void MicroserviceController::handlePost(http_request message) {
std::cout<< "handlePost(http_request message) " << std::endl;
std::cout<< message.to_string() << std::endl;
std::cout<< message.extract_json().get() << std::endl;
std::ofstream o("hello.json");
o << message.extract_json().get()<< std::endl;
after running that, i got an empty hello file.
I'm sure there is a simple solution to this, i just have a lack in http_request methods .
Thanks in advance for your help.
I am running across an intermittent deadlock in initialization of my Qt program, while it's relatively idle and in the event loop.
I confirmed this was a futex_wait by using strace, and I always see an entry like this for the main thread:
[pid 1768] futex(0x7f28f400aecc, FUTEX_WAIT_PRIVATE, 1, NULL
I found out that I could add a Qt event filter, and therefore log events as they are processed from the main thread. Here's a snippet for that:
bool ActivityEventFilter::eventFilter(QObject *obj, QEvent *ev){
VLOG(5) << "inspecting event " << obj << " name " << obj->objectName().toStdString() << " class " << obj->metaObject()->className() << " eventType " << ev->type();
if (VLOG_IS_ON(5))
{
if (ev->type() == QEvent::MetaCall)
{
if (QString(obj->metaObject()->className()).contains("QDBusConnectionPrivate")
{
VLOG(5) << "Dumping object data";
obj->dumpObjectInfo();
obj->dumpObjectTree();
}
}
}
...
}
And examining my logging, the freeze always is preceded by these lines:
I0614 17:18:27.928889 1812 ActivityEventFilter.cpp:19] inspecting event 0x7fde200144c0 name class QDBusConnectionPrivate eventType 43
I0614 17:18:27.928901 1812 ActivityEventFilter.cpp:28] Dumping object data
There is no output from the object info or from the object tree.
I am not consciously using DBus anywhere in my application, so I'm looking to find a way to track down who is calling it to see if it can be prevented, but without the objectInfo, I don't know where to start.
I found two Qt bug reports:
https://bugreports.qt.io/browse/QTBUG-42189
https://bugreports.qt.io/browse/QTBUG-51648
The first looks like the problem, but it appears all the resolutions went into Qt 5.4.0. (I'm using Qt 5.4.1)
The second looks a bit different.
My program (in C++) uses libev event loop. And I need to watch on a specific folder (say foo) for new files.
I cannot use Inotify::WaitForEvents() in block mode because I do not want to block my libev event loop. As suggested in inotify documentation,I use Inotify::SetNonBlock(true) to make it non-block. The inotify file descriptor is then passed to libev EV_STAT to watch on (as suggested in libev documentation).
The libev callback for EV_STAT is indeed called when there are new files in the folder foo. However, when I use Inotify::WaitForEvents() followed by Inotify::GetEventCount(), I get zero event.
I suspect that libev already consumed the event and convert it to EV_STAT event. If this is the case, how can I get the names of those new files?
I knew there is inode number in EV_STAT callback parameters, but getting file name from inode number is not trivial. So it is better if I can get file name instead.
Any suggestions?
EDIT
I wrote a small program to reproduce this problem. It seems the events are not lost. Instead, inotify events do not come yet when libev callback is called. The event can re-appear when you copy in a new file.
The program to reproduce the issue:
#include <ev++.h>
#include "inotify-cxx.h"
#include <iostream>
const char * path_to_watch = "/path/to/my/folder";
class ev_inotify_test
{
InotifyWatch m_watch;
Inotify m_notify;
// for watching new files
ev::stat m_folderWatcher;
public:
ev_inotify_test() : m_watch(path_to_watch, IN_MOVED_TO | IN_CLOSE_WRITE),
m_notify()
{
}
void run()
{
try {
start();
// run the loop
ev::get_default_loop().run(0);
}
catch (InotifyException & e) {
std::cout << e.GetMessage() << std::endl;
}
catch (...) {
std::cout << "got an unknown exception." << std::endl;
}
}
private:
void start()
{
m_notify.SetNonBlock(true);
m_notify.Add(m_watch);
m_folderWatcher.set<ev_inotify_test, &ev_inotify_test::cb_stat>(this);
m_folderWatcher.set(path_to_watch);
m_folderWatcher.start();
}
void cb_stat(ev::stat &w, int revents)
{
std::cout << "cb_stat called" << std::endl;
try {
m_notify.WaitForEvents();
size_t count = m_notify.GetEventCount();
std::cout << "inotify got " << count << " event(s).\n";
while (count > 0) {
InotifyEvent event;
bool got_event = m_notify.GetEvent(&event);
std::cout << "inotify confirm got event" << std::endl;
if (got_event) {
std::string filename = event.GetName();
std::cout << "test: inotify got file " << filename << std::endl;
}
--count;
}
}
catch (InotifyException &e) {
std::cout << "inotify exception occurred: " << e.GetMessage() << std::endl;
}
catch (...) {
std::cout << "Unknown exception in inotify processing occurred!" << std::endl;
}
}
};
int main(int argc, char ** argv)
{
ev_inotify_test().run();
}
When I copy in a tiny file (say 300 bytes), the file is detected immediately. But if I copy a bigger file (say 500 kB), there is no event until I copy another file in and then I get two events.
The output looks like:
cb_stat called # test_file_1 (300 bytes) is copied in
inotify got 1 event(s).
inotify confirm got event
test: inotify got file test_file_1
cb_stat called # test_file_2 (500 KB) is copied in
inotify got 0 event(s). # no inotify event
cb_stat called # test_file_3 (300 bytes) is copied in
inotify got 2 event(s).
inotify confirm got event
test: inotify got file test_file_2
inotify confirm got event
test: inotify got file test_file_3
I finally figured out the problem: I should use ev::io to watch the file descriptor of inotify, instead of using ev::stat to watch the folder.
In the example code, the definition of m_folderWatcher should be:
ev::io m_folderWatcher;
instead of
ev::stat m_folderWatcher;
And it should be initialized as:
m_folderWatcher.set(m_notify.GetDescriptor(), ev::READ);
instead of
m_folderWatcher.set(path_to_watch);
I am building a qt framework to download and install application updates (like sparkle for obj-c). The download works, the downloaded zip file is valid and i can extract the contents manually but when I let my framework unzip the contents via quazip the files (dll and exe) contains this and only this string: "MZ" and a special char which is wrong encoded (some kind of square on windows and "ê" on mac), so exactly 3 bytes. When I include a text file (or xml) in the zip file, it will be unzipped correctly, manually and with quazip, so I assume that the library was compiled correctly. Where is my error?
I think this can be part of the solution http://en.wikipedia.org/wiki/DOS_MZ_executable?
Here is my method to install the update:
QuaZip archiveWrapper(filename); // The downloaded zip file
if (archiveWrapper.open(QuaZip::mdUnzip)) {
QuaZipFile archive(&archiveWrapper);
qDebug() << "Extracting files" << archiveWrapper.getFileNameList();
for (bool more = archiveWrapper.goToFirstFile(); more; more = archiveWrapper.goToNextFile()) {
QString filePath = archiveWrapper.getCurrentFileName();
QString destinationPath = QDir::cleanPath(QDir::currentPath() + QDir::separator() + filePath);
QString destinationBackup = destinationPath + "_backup";
qDebug() << "Extract" << filePath << "to" << destinationPath;
QuaZipFile zip(archive.getZipName(), filePath);
zip.open(QIODevice::ReadOnly);
QByteArray data = zip.readAll();
zip.close();
QFile oldFile(destinationPath);
if (oldFile.exists()) {
qDebug() << "Rename" << destinationPath << "to" << destinationBackup;
if (!oldFile.rename(destinationBackup)) {
qWarning("Could not rename %s to %s!", destinationPath.toUtf8().constData(), destinationBackup.toUtf8().constData());
}
}
QFile destination(destinationPath);
destination.open(QIODevice::WriteOnly);
destination.write(data.data());
destination.close();
if (oldFile.exists()) {
qDebug() << "Deleting backup of" << destinationPath;
if (!oldFile.remove()) {
qWarning("Could not delete %s!", destinationPath.toUtf8().constData());
}
}
}
if (archive.getZipError() == UNZ_OK) {
qDebug() << "All files extracted successfully";
qDebug() << "Restarting application...";
archiveWrapper.close();
qApp->quit();
QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
} else {
qWarning("Error while extracting files (Error %d)", archive.getZipError());
archiveWrapper.close();
}
} else {
qWarning("Could not open archive to extract contents");
}
Edit:
I found out that data (QByteArray) has the expected size, so I think the problem is that QFile does not write the contents of QByteArray into the exe/dll files the way it should be?
Edit 2:
I've found one error, the file size to write:
destination.write(data.data(), data.size());
instead of
destination.write(data.data());
but still, the exe does not have an icon or is executable (but with the correct file size). For a short time a dos window opens and closes. There is a antivirus software running but there is no alert (and because this is a corporate notebook i am not able to shut it down and the update framework should also be running whether there is a antivirus software running or not).
Edit 3:
Although I thought writing exe files is complicated, it was a mixture of stupid bugs I "implemented" for testing purposes. So the simple
QFile destination(destinationPath);
destination.open(QIODevice::WriteOnly);
destination.write(data.data(), data.size())
is sufficient.
Working on a Qt application. I'm trying to get the exe file to return an md5 checksum of itself while it's running. How can I do this?
I tried this:
QFile theFile("file.exe");
QByteArray thisFile;
if (theFile.open(QIODevice::ReadOnly))
{
thisFile = theFile.readAll();
}
else
{
qDebug() << "Can't open";
}
qDebug() << QString("%1").arg(thisFile.length());
fileMd5 = QString(QCryptographicHash::hash((thisFile), QCryptographicHash::Md5).toHex().toUpper());
qDebug() << fileMd5;
This does not return the correct value, however.
Update:
I got it working with other files. The problem seems to be that I am unable to read the exe while it's running.
Final update:
This is the solution:
QFile theFile(QCoreApplication::applicationFilePath());
QByteArray thisFile;
if (theFile.open(QIODevice::ReadOnly))
{
thisFile = theFile.readAll();
}
else
{
qDebug() << "Can't open file.";
}
QString fileMd5 = QString(QCryptographicHash::hash((thisFile), QCryptographicHash::Md5).toHex());
qDebug() << fileMd5;
You forgot to call open on theFile.
if (!theFile.open(QIODevice::ReadOnly))
// Handle error here
Also, you should be using QCoreApplication::applicationFilePath() to get the path to the executable.
You have to create an independent application (let's call it myApp) which check the MD5sum and compare it with your PHP script and ask for an update if needed or load directly the application.
Like so : myApp=> need update ? (update) : (TheRealApp)
Ok, looks like it just wasn't finding the file. I tried an absolute path instead of a relative and it worked. I'll have to figure out what's going wrong, but it looks like it can read itself while running.