void StorageTracker::syncSetOriginDetails(const String& originIdentifier, const String& databaseFile) { ASSERT(!isMainThread()); MutexLocker lockDatabase(m_databaseGuard); openTrackerDatabase(true); if (!m_database.isOpen()) return; SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.ascii().data()); return; } statement.bindText(1, originIdentifier); statement.bindText(2, databaseFile); if (statement.step() != SQLResultDone) LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.ascii().data()); { MutexLocker lockOrigins(m_originSetGuard); if (!m_originSet.contains(originIdentifier)) m_originSet.add(originIdentifier); } { MutexLocker lockClient(m_clientGuard); if (m_client) m_client->dispatchDidModifyOrigin(originIdentifier); } }
void watchman_client_subscription::processSubscription() { auto client = lockClient(); if (!client) { watchman::log( watchman::ERR, "encountered a vacated client while running subscription rules\n"); return; } sub_action action; w_string policy_name; std::tie(action, policy_name) = get_subscription_action(this, root); if (action != sub_action::no_sync_needed) { bool executeQuery = true; if (action == sub_action::drop) { // fast-forward over any notifications while in the drop state auto position = root->view()->getMostRecentRootNumberAndTickValue(); last_sub_tick = position.ticks; query->since_spec = watchman::make_unique<ClockSpec>(position); watchman::log( watchman::DBG, "dropping subscription notifications for ", name, " until state ", policy_name, " is vacated. Advanced ticks to ", last_sub_tick, "\n"); executeQuery = false; } else if (action == sub_action::defer) { watchman::log( watchman::DBG, "deferring subscription notifications for ", name, " until state ", policy_name, " is vacated\n"); executeQuery = false; } else if (vcs_defer && root->view()->isVCSOperationInProgress()) { watchman::log( watchman::DBG, "deferring subscription notifications for ", name, " until VCS operations complete\n"); executeQuery = false; } if (executeQuery) { last_sub_tick = runSubscriptionRules(client.get(), root).position().ticks; } } else { watchman::log(watchman::DBG, "subscription ", name, " is up to date\n"); } }
void StorageTracker::syncDeleteAllOrigins() { ASSERT(!isMainThread()); MutexLocker lockDatabase(m_databaseGuard); openTrackerDatabase(false); if (!m_database.isOpen()) return; SQLiteStatement statement(m_database, "SELECT origin, path FROM Origins"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Failed to prepare statement."); return; } int result; while ((result = statement.step()) == SQLResultRow) { if (!canDeleteOrigin(statement.getColumnText(0))) continue; SQLiteFileSystem::deleteDatabaseFile(statement.getColumnText(1)); { MutexLocker lockClient(m_clientGuard); if (m_client) m_client->dispatchDidModifyOrigin(statement.getColumnText(0)); } } if (result != SQLResultDone) LOG_ERROR("Failed to read in all origins from the database."); if (m_database.isOpen()) m_database.close(); if (!SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath())) { // In the case where it is not possible to delete the database file (e.g some other program // like a virus scanner is accessing it), make sure to remove all entries. openTrackerDatabase(false); if (!m_database.isOpen()) return; SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins"); if (deleteStatement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of all origins"); return; } if (!deleteStatement.executeCommand()) { LOG_ERROR("Unable to execute deletion of all origins"); return; } } SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath); }
void StorageTracker::syncImportOriginIdentifiers() { ASSERT(m_isActive); ASSERT(!isMainThread()); { MutexLocker lockDatabase(m_databaseGuard); // Don't force creation of StorageTracker's db just because a tracker // was initialized. It will be created if local storage dbs are found // by syncFileSystemAndTrackerDatabse() or the next time a local storage // db is created by StorageAreaSync. openTrackerDatabase(false); if (m_database.isOpen()) { SQLiteStatement statement(m_database, "SELECT origin FROM Origins"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Failed to prepare statement."); return; } int result; { MutexLocker lockOrigins(m_originSetGuard); while ((result = statement.step()) == SQLResultRow) m_originSet.add(statement.getColumnText(0).threadsafeCopy()); } if (result != SQLResultDone) { LOG_ERROR("Failed to read in all origins from the database."); return; } } } syncFileSystemAndTrackerDatabase(); { MutexLocker lockClient(m_clientGuard); if (m_client) { MutexLocker lockOrigins(m_originSetGuard); OriginSet::const_iterator end = m_originSet.end(); for (OriginSet::const_iterator it = m_originSet.begin(); it != end; ++it) m_client->dispatchDidModifyOrigin(*it); } } }
watchman_client_subscription::~watchman_client_subscription() { auto client = lockClient(); if (client) { client->unsubByName(name); } }
void StorageTracker::setClient(StorageTrackerClient* client) { MutexLocker lockClient(m_clientGuard); m_client = client; }
void StorageTracker::syncDeleteOrigin(const String& originIdentifier) { ASSERT(!isMainThread()); MutexLocker lockDatabase(m_databaseGuard); if (!canDeleteOrigin(originIdentifier)) { LOG_ERROR("Attempted to delete origin '%s' while it was being created\n", originIdentifier.ascii().data()); return; } openTrackerDatabase(false); if (!m_database.isOpen()) return; // Get origin's db file path, delete entry in tracker's db, then delete db file. SQLiteStatement pathStatement(m_database, "SELECT path FROM Origins WHERE origin=?"); if (pathStatement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare selection of path for origin '%s'", originIdentifier.ascii().data()); return; } pathStatement.bindText(1, originIdentifier); int result = pathStatement.step(); if (result != SQLResultRow) { LOG_ERROR("Unable to find origin '%s' in Origins table", originIdentifier.ascii().data()); return; } String path = pathStatement.getColumnText(0); ASSERT(!path.isEmpty()); SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins where origin=?"); if (deleteStatement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of origin '%s'", originIdentifier.ascii().data()); return; } deleteStatement.bindText(1, originIdentifier); if (!deleteStatement.executeCommand()) { LOG_ERROR("Unable to execute deletion of origin '%s'", originIdentifier.ascii().data()); return; } SQLiteFileSystem::deleteDatabaseFile(path); bool shouldDeleteTrackerFiles = false; { MutexLocker originLock(m_originSetGuard); m_originSet.remove(originIdentifier); shouldDeleteTrackerFiles = m_originSet.isEmpty(); } if (shouldDeleteTrackerFiles) { m_database.close(); SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath()); SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath); } { MutexLocker lockClient(m_clientGuard); if (m_client) m_client->dispatchDidModifyOrigin(originIdentifier); } }