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