Source

NPAPI-chrome-file-api / X11 / FileWatcherTaskX11.cpp

Full commit
/*
 * FileWatcherTaskX11.cpp
 *
 *  Created on: 22/05/2012
 *      Author: ryan
 */



#include "FileWatcherTaskX11.h"
#include <boost/thread.hpp>
#include <sys/inotify.h>
#include <iostream>
#include "variant_list.h"

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

using namespace boost::filesystem;

void FileWatcherTaskX11::Watch(boost::filesystem::path p, FB::JSObjectPtr callback)
{
	boost::thread thread = boost::thread(boost::bind(&FileWatcherTaskX11::ListenForFileEvents, this, p, callback));
	thread.detach();
}

void FileWatcherTaskX11::ListenForFileEvents(boost::filesystem::path path, FB::JSObjectPtr callback)
{
	int length;//, i = 0;
	int fd;
	int wd;
	char buffer[EVENT_BUF_LEN];

	/*creating the INOTIFY instance*/
	fd = inotify_init();

	/*checking for error*/
	if ( fd < 0 ) {
		std::cerr << "inotify_init" ;
	}

	std::map<int, boost::filesystem::path*> wdMap;

	/*adding the  directory into watch list. Here, the suggestion is to validate the existence of the directory before adding into monitoring list.*/
	//wd = inotify_add_watch( fd, path.c_str(), IN_MODIFY | IN_MOVED_TO);
	this->AddWatchToDir(fd, path, wdMap );

	/*read to determine the event change happens on “/tmp” directory. Actually this read blocks until the change event occurs*/
	while (!this->_ShouldStop())
	{
		int i = 0;
		struct timeval time;
		fd_set set;

		time.tv_sec = 10;
		time.tv_usec = 0;

		FD_ZERO(&set);
		FD_SET(fd, &set);

		int sr = select (FD_SETSIZE, &set, NULL, NULL, &time);
		if (!sr) //timeout
		{
			continue;
		}
		else if (sr > 0)
		{
			length = read( fd, buffer, EVENT_BUF_LEN );

			/*checking for error*/
			if ( length < 0 )
			{
				std::cerr << "read";
			}

			/*actually read return the list of change events happens. Here, read the change event one by one and process it accordingly.*/
			while ( i < length )
			{
				struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
				if ( event->len )
				{
					if ( ((event->mask & IN_MODIFY) || (event->mask & IN_MOVED_TO)) &&  !(event->mask & IN_ISDIR) )
					{
						std::string filename = event->name;
						if (filename[0] != '.')
						{
							boost::filesystem::path* root = wdMap[event->wd];
							boost::filesystem::path fsPath = *root / filename;
							callback->Invoke("", FB::variant_list_of(fsPath.generic_string()));
						}
					}
				}
				i += EVENT_SIZE + event->len;
			}
		}
		else
		{
			std::cerr << "select";
			break;
		}
	}
	/*removing the “/tmp” directory from the watch list.*/
	std::map<int, boost::filesystem::path*>::iterator it;
	for (it = wdMap.begin(); it != wdMap.end(); it++)
	{
		inotify_rm_watch( fd, (*it).first );
		delete (*it).second;
	}
	//inotify_rm_watch( fd, wd );

	/*closing the INOTIFY instance*/
	close( fd );
}

void FileWatcherTaskX11::AddWatchToDir(int fd, boost::filesystem::path p, std::map<int, boost::filesystem::path*> &watchDescriptors)
{
	int wd = inotify_add_watch( fd, p.generic_string().c_str(), IN_MODIFY | IN_MOVED_TO);
	watchDescriptors[wd] = new path(p);

	directory_iterator di(p);
	directory_iterator eos;

	while(di != eos)
	{
		directory_entry de = *di;
		if (is_directory(de.status())){
			this->AddWatchToDir(fd, de.path(), watchDescriptors);
		}
		di++;
	}
}

void FileWatcherTaskX11::StopWatching()
{
	this->stop = 1;
}

bool FileWatcherTaskX11::_ShouldStop()
{
	return this->stop != 0;
}