def __init__(self, eq, fs, ignore_config=None, timeout=1):
     self.log = logging.getLogger('ubuntuone.SyncDaemon.FSMonitor')
     self.log.setLevel(TRACE)
     self._processor = NotifyProcessor(self, ignore_config)
     self.fs = fs
     self.eq = eq
     self._factory = PyInotifyEventsFactory(self._processor)
     self._protocol = None
class FilesystemMonitor(object):
    """Implementation that allows to receive events from the system."""

    def __init__(self, eq, fs, ignore_config=None, timeout=1):
        self.log = logging.getLogger('ubuntuone.SyncDaemon.FSMonitor')
        self.log.setLevel(TRACE)
        self._processor = NotifyProcessor(self, ignore_config)
        self.fs = fs
        self.eq = eq
        self._factory = PyInotifyEventsFactory(self._processor)
        self._protocol = None

    @classmethod
    def is_available_monitor(cls):
        """Return if the monitor can be used in the platform."""
        # can only be used if the daemon is running
        return is_daemon_running()

    @defer.inlineCallbacks
    def _connect_to_daemon(self):
        """Connect to the daemon so that we can receive events."""
        description = 'unix:path=%s' % DAEMON_SOCKET
        client = endpoints.clientFromString(reactor, description)
        self._protocol = yield client.connect(self._factory)
        # add the user with no paths
        yield self._protocol.add_user([])

    def add_to_mute_filter(self, event, **info):
        """Add info to mute filter in the processor."""
        self._processor.add_to_mute_filter(event, info)

    def rm_from_mute_filter(self, event, **info):
        """Remove info to mute filter in the processor."""
        self._processor.rm_from_mute_filter(event, info)

    def shutdown(self):
        """Prepares the EQ to be closed."""
        if self._protocol is not None:

            def on_user_removed(data):
                """We managed to remove the user."""
                self._protocol.transport.loseConnection()
                self._protocol = None
                return True

            def on_user_not_removed(reason):
                """We did not manage to remove the user."""
                return True

            d = self._protocol.remove_user()
            d.addCallback(on_user_removed)
            d.addErrback(on_user_not_removed)
            return d
        return defer.succeed(True)

    @defer.inlineCallbacks
    def rm_watch(self, dirpath):
        """Remove watch from a dir."""
        # in mac os x we are only watching the parent watches, this is an
        # important details because we will only send a real remove_path to the
        # daemon if the path is the parent path else we will filter it in the
        # factory level

        if not dirpath[-1] == os.path.sep:
            dirpath += os.path.sep

        if dirpath not in self._factory.watched_paths:
            # we are watching a parent path but we are not a root one
            # therefore we are going to add it as an ignored path and
            # return
            self._factory.ignored_paths.append(dirpath)
            defer.returnValue(None)

        # if we got to this point we want to remove a root dir, this is an
        # important detail to take care of. Connect if needed and tell the
        # daemon to remove the path
        if self._protocol is None:
            # we have not yet connected, lets do it!
            yield self._connect_to_daemon()
        was_removed = yield self._protocol.remove_path(dirpath)
        # only remove it if we really removed it
        if was_removed:
            self._factory.watched_paths.remove(dirpath)

    @defer.inlineCallbacks
    def add_watch(self, dirpath):
        """Add watch to a dir."""
        if not dirpath[-1] == os.path.sep:
            dirpath = dirpath + os.path.sep

        # if we are watching a parent dir we can just ensure that it is not
        # ignored
        parent_watched = any(dirpath.startswith(watched_path)
                             for watched_path in self._factory.watched_paths)
        if parent_watched:
            if dirpath in self._factory.ignored_paths:
                self._factory.ignored_paths.remove(dirpath)
            defer.returnValue(True)

        if dirpath in self._factory.ignored_paths:
            self._factory.ignored_paths.remove(dirpath)
            defer.returnValue(True)

        if self._protocol is None:
            # we have not yet connected, lets do it!
            yield self._connect_to_daemon()

        was_added = yield self._protocol.add_path(dirpath)
        if was_added:
            self._factory.watched_paths.append(dirpath)
            defer.returnValue(True)

    def add_watches_to_udf_ancestors(self, volume):
        """Add a inotify watch to volume's ancestors if it's an UDF."""
        # On Mac OS X we do no need to add watches to the ancestors because we
        # will get the events from them with no problem.
        return defer.succeed(True)

    def is_frozen(self):
        """Checks if there's something frozen."""
        return self._processor.frozen_path is not None

    def freeze_begin(self, path):
        """Puts in hold all the events for this path."""
        if self._processor.frozen_path is not None:
            raise ValueError("There's something already frozen!")
        self._processor.freeze_begin(path)

    def freeze_rollback(self):
        """Unfreezes the frozen path, reseting to idle state."""
        if self._processor.frozen_path is None:
            raise ValueError("Rolling back with nothing frozen!")
        self._processor.freeze_rollback()

    def freeze_commit(self, events):
        """Unfreezes the frozen path, sending received events if not dirty.

        If events for that path happened:
            - return True
        else:
            - push the here received events, return False
        """
        if self._processor.frozen_path is None:
            raise ValueError("Committing with nothing frozen!")

        d = defer.execute(self._processor.freeze_commit, events)
        return d