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

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



#include "FileWatcherTaskX11.h"
#include <sys/inotify.h>
#include <sys/signal.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)
{
	thread = boost::thread(boost::bind(&FileWatcherTaskX11::ListenForFileEvents, this, p, callback));
	//thread.detach();
}

/* Signal handler that does nothing. This is necessary because
 * I send a signal to interrupt pselect. Without a signal handler for my signal,
 * the process will crash. I only care about interrupting the pselect.
 * */
static void hdl (int sig)
{
	//do nothing
}

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

	memset (&act, 0, sizeof(act));
	act.sa_handler = hdl;

	/* This server should shut down on SIGTERM. */
	if (sigaction(SIGUSR1, &act, 0)) {
		std::cerr << ("sigaction");
		return;
	}

	sigemptyset (&mask);
    sigaddset (&mask, SIGUSR1);

    if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
		std::cerr << "sigprocmask";
		return;
	}

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

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

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

	this->AddWatchToDir(fd, path, wdMap );

	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 = pselect (FD_SETSIZE, &set, NULL, NULL, NULL, &orig_mask);
		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;
		}
	}
	//cleanup
	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;
	}

	/*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;
	pthread_kill(thread.native_handle(), SIGUSR1);

	boost::posix_time::time_duration timeout = boost::posix_time::seconds(5);
	thread.timed_join(timeout);
}

bool FileWatcherTaskX11::_ShouldStop()
{
	return this->stop != 0;
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.