Exemple #1
0
/**
 * @return NULL on error, a newly allocated string via halloc() otherwise.
 */
static char *
uhc_get_next(void)
{
	struct uhc *uhc;
	char *host;
	time_t now;
	size_t n;

	g_return_val_if_fail(uhc_list, NULL);

	now = tm_time();

	n = hash_list_count(uhc_list);
	if (0 == n)
		return NULL;

	/*
	 * Wait UHC_RETRY_AFTER secs before contacting the UHC again.
	 * Can't be too long because the UDP reply may get lost if the
	 * requesting host already has a saturated b/w.
	 * If we come here, it's because we're lacking hosts for establishing
	 * a Gnutella connection, after we exhausted our caches.
	 */

	while (n-- != 0) {
		uhc = hash_list_head(uhc_list);

		g_assert(uhc != NULL);	/* We computed count on entry */

		if (delta_time(now, uhc->stamp) >= UHC_RETRY_AFTER)
			goto found;

		hash_list_moveto_tail(uhc_list, uhc);
	}

	return NULL;

found:
	uhc->stamp = now;
	host = h_strdup(uhc->host);

	if (uhc->used < UHC_MAX_ATTEMPTS) {
		uhc->used++;
		hash_list_moveto_tail(uhc_list, uhc);
	} else {
		hash_list_remove(uhc_list, uhc);
		uhc_free(&uhc);
	}

	return host;
}
Exemple #2
0
/**
 * Write value to the database file, possibly caching it and deferring write.
 *
 * Any registered value cleanup callback will be invoked right after the value
 * is written to disk (for immediated writes) or removed from the cache (for
 * deferred writes).
 *
 * @param dw		the DBM wrapper
 * @param key		the key (constant-width, determined at open time)
 * @param value		the start of the value in memory
 * @param length	length of the value
 */
void
dbmw_write(dbmw_t *dw, gconstpointer key, gpointer value, size_t length)
{
	struct cached *entry;

	dbmw_check(dw);
	g_assert(key);
	g_assert(length <= dw->value_size);
	g_assert(length || value == NULL);
	g_assert(length == 0 || value);

	dw->w_access++;

	entry = map_lookup(dw->values, key);
	if (entry) {
		if (entry->dirty)
			dw->w_hits++;
		else if (entry->absent)
			dw->count_needs_sync = TRUE;	/* Key exists now */
		fill_entry(dw, entry, value, length);
		hash_list_moveto_tail(dw->keys, key);
	} else if (dw->max_cached > 1) {
		entry = allocate_entry(dw, key, NULL);
		fill_entry(dw, entry, value, length);
		dw->count_needs_sync = TRUE;	/* Does not know whether key exists */
	} else { 
		write_immediately(dw, key, value, length);
	}
}
Exemple #3
0
/**
 * Delete key from database.
 */
void
dbmw_delete(dbmw_t *dw, gconstpointer key)
{
	struct cached *entry;

	dbmw_check(dw);
	g_assert(key);

	dw->w_access++;

	entry = map_lookup(dw->values, key);
	if (entry) {
		if (entry->dirty)
			dw->w_hits++;
		if (!entry->absent) {
			dw->count_needs_sync = TRUE;	/* Deferred delete */
			fill_entry(dw, entry, NULL, 0);
			entry->absent = TRUE;
		}
		hash_list_moveto_tail(dw->keys, key);
	} else {
		dw->ioerr = FALSE;
		dbmap_remove(dw->dm, key);

		if (dbmap_has_ioerr(dw->dm)) {
			dw->ioerr = TRUE;
			dw->error = errno;
			g_warning("DBMW \"%s\" I/O error whilst deleting key: %s",
				dw->name, dbmap_strerror(dw->dm));
		}

		/*
		 * If the maximum value length of the DB is 0, then it is used as a
		 * "search table" only, meaning there will be no read to get values,
		 * only existence checks.
		 *
		 * Therefore, it makes sense to cache that the key is no longer valid.
		 * Otherwise, possibly pushing a value out of the cache to record
		 * a deletion is not worth it.
		 */

		if (0 == dw->value_size) {
			WALLOC0(entry);
			entry->absent = TRUE;
			(void) allocate_entry(dw, key, entry);
		}
	}
}
Exemple #4
0
/**
 * Write value to the database file, possibly caching it and deferring write.
 *
 * Any registered value cleanup callback will be invoked right after the value
 * is written to disk (for immediated writes) or removed from the cache (for
 * deferred writes).
 *
 * @param dw		the DBM wrapper
 * @param key		the key (constant-width, determined at open time)
 * @param value		the start of the value in memory
 * @param length	length of the value
 */
void
dbmw_write(dbmw_t *dw, const void *key, void *value, size_t length)
{
	struct cached *entry;

	dbmw_check(dw);
	g_assert(key);
	g_assert(length <= dw->value_size);
	g_assert(length || value == NULL);
	g_assert(length == 0 || value);

	dw->w_access++;

	entry = map_lookup(dw->values, key);
	if (entry) {
		if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_UPDATE)) {
			dbg_ds_log(dw->dbg, dw, "%s: %s key=%s%s",
				G_STRFUNC, entry->dirty ? "dirty" : "clean",
				dbg_ds_keystr(dw->dbg, key, (size_t) -1),
				entry->absent ? " (was absent)" : "");
		}

		if (entry->dirty)
			dw->w_hits++;
		if (entry->absent)
			dw->cached++;			/* Key exists now, in unflushed status */
		fill_entry(dw, entry, value, length);
		hash_list_moveto_tail(dw->keys, key);

	} else if (dw->max_cached > 1) {
		if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_UPDATE)) {
			dbg_ds_log(dw->dbg, dw, "%s: deferring key=%s",
				G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1));
		}

		entry = allocate_entry(dw, key, NULL);
		fill_entry(dw, entry, value, length);
		dw->count_needs_sync = TRUE;	/* Does not know whether key exists */

	} else { 
		if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_UPDATE)) {
			dbg_ds_log(dw->dbg, dw, "%s: writing key=%s",
				G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1));
		}

		write_immediately(dw, key, value, length);
	}
}
Exemple #5
0
/**
 * Callback for adns_resolve(), invoked when the resolution is complete.
 */
static void
uhc_host_resolved(const host_addr_t *addrs, size_t n, void *uu_udata)
{
	(void) uu_udata;
	g_assert(addrs);

	/*
	 * If resolution failed, try again if possible.
	 */

	if (0 == n) {
		if (GNET_PROPERTY(bootstrap_debug))
			g_warning("could not resolve UDP host cache \"%s\"",
				uhc_ctx.host);

		uhc_try_next();
		return;
	}

	if (n > 1) {
		size_t i;
		host_addr_t *hav;
		/* Current UHC was moved to tail by uhc_get_next() */
		struct uhc *uhc = hash_list_tail(uhc_list);

		/*
		 * UHC resolved to multiple endpoints. Could be roundrobbin or
		 * IPv4 and IPv6 addresss. Adding them as seperate entries: if the
		 * IPv6 is unreachable we have an opportunity to skip it.
		 * 		-- JA 24/7/2011
		 *
		 * Shuffle the address array before appending them to the UHC list.
		 *		--RAM, 2015-10-01
		 */

		hav = HCOPY_ARRAY(addrs, n);
		SHUFFLE_ARRAY_N(hav, n);

		for (i = 0; i < n; i++) {
			const char *host = host_addr_port_to_string(hav[i], uhc_ctx.port);
			g_debug("BOOT UDP host cache \"%s\" resolved to %s (#%zu)",
				uhc_ctx.host, host, i + 1);

			uhc_list_append(host);
		}

		hash_list_remove(uhc_list, uhc);	/* Replaced by IP address list */
		uhc_free(&uhc);

		/*
		 * We're going to continue and process the first address (in our
		 * shuffled array).  Make sure it is put at the end of the list
		 * and marked as being used, mimicing what uhc_get_next() would do.
		 *		--RAM, 2015-10-01
		 */

		{
			struct uhc key;

			key.host = host_addr_port_to_string(hav[0], uhc_ctx.port);
			uhc = hash_list_lookup(uhc_list, &key);
			g_assert(uhc != NULL);	/* We added the entry above! */
			uhc->stamp = tm_time();
			uhc->used++;
			hash_list_moveto_tail(uhc_list, uhc);
		}

		uhc_ctx.addr = hav[0];		/* Struct copy */
		HFREE_NULL(hav);
	} else {
		uhc_ctx.addr = addrs[0];
	}

	if (GNET_PROPERTY(bootstrap_debug))
		g_debug("BOOT UDP host cache \"%s\" resolved to %s",
			uhc_ctx.host, host_addr_to_string(uhc_ctx.addr));


	/*
	 * Now send the ping.
	 */

	uhc_send_ping();
}
Exemple #6
0
/**
 * Delete key from database.
 */
void
dbmw_delete(dbmw_t *dw, const void *key)
{
	struct cached *entry;

	dbmw_check(dw);
	g_assert(key);

	dw->w_access++;

	entry = map_lookup(dw->values, key);
	if (entry) {
		if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_DELETE)) {
			dbg_ds_log(dw->dbg, dw, "%s: %s key=%s%s",
				G_STRFUNC, entry->dirty ? "dirty" : "clean",
				dbg_ds_keystr(dw->dbg, key, (size_t) -1),
				entry->absent ? " (was absent)" : "");
		}

		if (entry->dirty)
			dw->w_hits++;
		if (!entry->absent) {
			/*
			 * Entry was present but is now deleted.
			 *
			 * If it was clean, then it was flushed to the database and we now
			 * know that there is one less entry in the database than there is
			 * physically present in the map.
			 *
			 * If it was dirty, then we do not know whether it exists in the
			 * database or not, and therefore we cannot adjust the amount
			 * of cached entries down.
			 */

			if (entry->dirty)
				dw->count_needs_sync = TRUE;	/* Deferred delete */
			else
				dw->cached--;					/* One less entry in database */

			fill_entry(dw, entry, NULL, 0);
			entry->absent = TRUE;
		}
		hash_list_moveto_tail(dw->keys, key);

	} else {
		if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_DELETE)) {
			dbg_ds_log(dw->dbg, dw, "%s: removing key=%s",
				G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1));
		}

		dw->ioerr = FALSE;
		dbmap_remove(dw->dm, key);

		if (dbmap_has_ioerr(dw->dm)) {
			dw->ioerr = TRUE;
			dw->error = errno;
			s_warning("DBMW \"%s\" I/O error whilst deleting key: %s",
				dw->name, dbmap_strerror(dw->dm));
		}

		/*
		 * If the maximum value length of the DB is 0, then it is used as a
		 * "search table" only, meaning there will be no read to get values,
		 * only existence checks.
		 *
		 * Therefore, it makes sense to cache that the key is no longer valid.
		 * Otherwise, possibly pushing a value out of the cache to record
		 * a deletion is not worth it.
		 */

		if (0 == dw->value_size) {
			WALLOC0(entry);
			entry->absent = TRUE;
			(void) allocate_entry(dw, key, entry);

			if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING)) {
				dbg_ds_log(dw->dbg, dw, "%s: cached absent key=%s",
					G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1));
			}
		}
	}
}