Как получить новые добавленные файлы в папке, используя Inotify вместе с libev?

Моя программа (на C++) использует цикл событий libev. И мне нужно смотреть на определенную папку (скажем, foo) для новых файлов.

Я не могу использовать Inotify::WaitForEvents() в режиме блокировки, потому что я не хочу блокировать цикл обработки событий libev. Как предлагается в документации inotify, я использую Inotify::SetNonBlock(true), чтобы сделать его неблокированным. Дескриптор файла inotify затем передается в libev EV_STAT для наблюдения (как предложено в документации к libev).

Обратный вызов libev для EV_STAT действительно вызывается, когда в папке foo появляются новые файлы. Однако, когда я использую Inotify::WaitForEvents(), за которым следует Inotify::GetEventCount(), я получаю нулевое событие.

Я подозреваю, что libev уже использовал событие и преобразовал его в событие EV_STAT. Если это так, как я могу получить имена этих новых файлов?

Я знал, что в параметрах обратного вызова EV_STAT есть номер инода, но получить имя файла по номеру инода нетривиально. Так что лучше, если я получу имя файла.

Какие-либо предложения?


РЕДАКТИРОВАТЬ

Я написал небольшую программу для воспроизведения этой проблемы. Кажется, события не потеряны. Вместо этого события inotify еще не наступают, когда вызывается callback libev. Событие может появиться снова при копировании в новый файл.

Программа для воспроизведения вопроса:

#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();
}

Когда я копирую крошечный файл (скажем, 300 байт), файл обнаруживается немедленно. Но если я скопирую файл большего размера (скажем, 500 КБ), события не произойдет, пока я не скопирую другой файл, и тогда я получу два события.

Вывод выглядит так:

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

1 ответ

Я наконец-то понял проблему: я должен использовать ev::io для просмотра файлового дескриптора inotify вместо использования ev::stat для просмотра папки.

В примере кода определение m_folderWatcher должно быть:

ev::io m_folderWatcher;

вместо

ev::stat m_folderWatcher;

И это должно быть инициализировано как:

m_folderWatcher.set(m_notify.GetDescriptor(), ev::READ);

вместо

m_folderWatcher.set(path_to_watch);
Другие вопросы по тегам