Exemplo n.º 1
0
/* Creates a simple query header.
 *
 * The ns argument selects which namespace to use. If it's not set, we use
 * "admin.$cmd". */
static mcon_str *create_simple_header(mongo_connection *con, char *ns)
{
	struct mcon_str *str;

	mcon_str_ptr_init(str);

	mcon_serialize_int(str, 0); /* We need to fill this with the length */

	mcon_serialize_int(str, mongo_connection_get_reqid(con));
	mcon_serialize_int(str, 0); /* Reponse to */
	mcon_serialize_int(str, 2004); /* OP_QUERY */

	mcon_serialize_int(str, MONGO_QUERY_FLAG_SLAVE_OK); /* Flags */
	mcon_str_addl(str, ns ? ns : "admin.$cmd", ns ? strlen(ns) + 1 : 11, 0);
	mcon_serialize_int(str, 0); /* Number to skip */
	mcon_serialize_int(str, -1); /* Number to return, has to be -1 for admin commands */

	return str;
}
Exemplo n.º 2
0
static mongo_connection *mongo_get_connection_multiple(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message)
{
	mongo_connection *con = NULL;
	mongo_connection *tmp;
	mcon_collection  *collection = NULL;
	mongo_read_preference tmp_rp; /* We only support NEAREST for MULTIPLE right now */
	int i;
	int found_connected_server = 0;
	mcon_str         *messages;
	int found_supported_wire_version = 1;

	mcon_str_ptr_init(messages);

	/* Create a connection to every of the servers in the seed list */
	for (i = 0; i < servers->count; i++) {
		int ismaster_error = 0;
		char *con_error_message = NULL;
		tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message);

		if (tmp) {
			found_connected_server++;

			/* Run ismaster, if needed, to extract server flags */
			ismaster_error = mongo_connection_ismaster(manager, tmp, &servers->options, NULL, NULL, NULL, &con_error_message, NULL);
			switch (ismaster_error) {
				case 1: /* Run just fine */
				case 2: /* ismaster() skipped due to interval */
					break;

				case 0: /* Some error */
				case 3: /* Run just fine, but hostname didn't match what we expected */
				case 4: /* Danger danger, reported wire version does not overlap what we support */
				default:
					mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "server_flags: error while getting the server configuration %s:%d: %s", servers->server[i]->host, servers->server[i]->port, con_error_message);
					/* If it failed because of wire version, we have to bail out completely
					 * later on, but we should continue to aggregate the errors in case more
					 * servers are unsupported */
					if (ismaster_error == 4) {
						mongo_manager_connection_deregister(manager, tmp);
						found_supported_wire_version = 0;
					} else {
						mongo_connection_destroy(manager, tmp, MONGO_CLOSE_BROKEN);
					}
					tmp = NULL;
					found_connected_server--;
			}
		}
		if (!tmp) {
			if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) {
				mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message);
				if (messages->l) {
					mcon_str_addl(messages, "; ", 2, 0);
				}
				mcon_str_add(messages, "Failed to connect to: ", 0);
				mcon_str_add(messages, servers->server[i]->host, 0);
				mcon_str_addl(messages, ":", 1, 0);
				mcon_str_add_int(messages, servers->server[i]->port);
				mcon_str_addl(messages, ": ", 2, 0);
				mcon_str_add(messages, con_error_message, 1); /* Also frees con_error_message */
			} else {
				free(con_error_message);
			}
		}
	}

	if (!found_supported_wire_version) {
		*error_message = strdup("Found a server running unsupported wire version. Please upgrade the driver, or take the server out of rotation");
		mcon_str_ptr_dtor(messages);
		return NULL;
	}

	/* If we don't have a connected server then there is no point in continueing */
	if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) {
		mcon_str_ptr_dtor(messages);
		return NULL;
	}

	/* When selecting a *mongos* node, readPreferences make no sense as we
	 * don't have a "primary" or "secondary" mongos. The mongos nodes aren't
	 * tagged either.  To pick a mongos we therefore simply pick the "nearest"
	 * mongos node. */
	tmp_rp.type = MONGO_RP_NEAREST;
	tmp_rp.tagsets = NULL;
	tmp_rp.tagset_count = 0;
	collection = mongo_find_candidate_servers(manager, &tmp_rp, servers);
	if (!collection || collection->count == 0) {
		if (messages->l) {
			*error_message = strdup(messages->d);
		} else {
			*error_message = strdup("No candidate servers found");
		}
		goto bailout;
	}
	collection = mongo_sort_servers(manager, collection, &servers->read_pref);
	collection = mongo_select_nearest_servers(manager, collection, &servers->options, &servers->read_pref);
	if (!collection) {
		*error_message = strdup("No server near us");
		goto bailout;
	}
	con = mongo_pick_server_from_set(manager, collection, &servers->read_pref);

bailout:
	/* Cleaning up */
	mcon_str_ptr_dtor(messages);
	if (collection) {
		mcon_collection_free(collection);	
	}
	return con;
}
Exemplo n.º 3
0
/**
 * Sends an ismaster command to the server and returns an array of new
 * connectable nodes
 *
 * Returns:
 * 0: when an error occurred
 * 1: when is master was run and worked
 * 2: when is master wasn't run due to the time-out limit
 * 3: when it all worked, but we need to remove the seed host (due to its name
 *    not being what the server thought it is) - in that case, the server in
 *    the last argument is changed
 */
int mongo_connection_ismaster(mongo_con_manager *manager, mongo_connection *con, char **repl_set_name, int *nr_hosts, char ***found_hosts, char **error_message, mongo_server_def *server)
{
	mcon_str      *packet;
	char          *data_buffer;
	char          *set = NULL;      /* For replicaset in return */
	char          *hosts, *ptr, *string;
	unsigned char  ismaster = 0, arbiter = 0;
	char          *connected_name, *we_think_we_are;
	struct timeval now;
	int            retval = 1;

	gettimeofday(&now, NULL);
	if ((con->last_ismaster + manager->ismaster_interval) > now.tv_sec) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: skipping: last ran at %ld, now: %ld, time left: %ld", con->last_ismaster, now.tv_sec, con->last_ismaster + manager->ismaster_interval - now.tv_sec);
		return 2;
	}

	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: start");
	packet = bson_create_ismaster_packet(con);

	if (!mongo_connect_send_packet(manager, con, packet, &data_buffer, error_message)) {
		return 0;
	}

	/* Find data fields */
	ptr = data_buffer + sizeof(int32_t); /* Skip the length */

	/* We find out whether the machine we connected too, is actually the
	 * one we thought we were connecting too */
	if (!bson_find_field_as_string(ptr, "me", &connected_name)) {
		struct mcon_str *tmp;

		mcon_str_ptr_init(tmp);
		mcon_str_add(tmp, "Host does not seem to be a replicaset member (", 0);
		mcon_str_add(tmp, mongo_server_hash_to_server(con->hash), 1);
		mcon_str_add(tmp, ")", 0);

		*error_message = strdup(tmp->d);
		mcon_str_ptr_dtor(tmp);

		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, *error_message);
		free(data_buffer);
		return 0;
	}

	we_think_we_are = mongo_server_hash_to_server(con->hash);
	if (strcmp(connected_name, we_think_we_are) == 0) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the server name matches what we thought it'd be (%s).", we_think_we_are);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the server name (%s) did not match with what we thought it'd be (%s).", connected_name, we_think_we_are);
		/* We reset the name as the server responded with a different name than
		 * what we thought it was */
		free(server->host);
		server->host = mcon_strndup(connected_name, strchr(connected_name, ':') - connected_name);
		server->port = atoi(strchr(connected_name, ':') + 1);
		retval = 3;
	}
	free(we_think_we_are);

	/* Do replica set name test */
	bson_find_field_as_string(ptr, "setName", &set);
	if (!set) {
		char *errmsg = NULL;
		bson_find_field_as_string(ptr, "errmsg", &errmsg);
		if (errmsg) {
			*error_message = strdup(errmsg);
		} else {
			*error_message = strdup("Not a replicaset member");
		}
		free(data_buffer);
		return 0;
	} else if (*repl_set_name) {
		if (strcmp(set, *repl_set_name) != 0) {
			struct mcon_str *tmp;

			mcon_str_ptr_init(tmp);
			mcon_str_add(tmp, "Host does not match replicaset name. Expected: ", 0);
			mcon_str_add(tmp, *repl_set_name, 0);
			mcon_str_add(tmp, "; Found: ", 0);
			mcon_str_add(tmp, set, 0);

			*error_message = strdup(tmp->d);
			mcon_str_ptr_dtor(tmp);

			free(data_buffer);
			return 0;
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the found replicaset name matches the expected one (%s).", set);
		}
	} else if (*repl_set_name == NULL) {
		/* This can happen, in case the replicaset name was not given, but just
		 * bool(true) (or string("1")) in the connection options. */
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the replicaset name is not set, so we're using %s.", set);
		*repl_set_name = strdup(set);
	}

	/* If the server definition has not set the repl_set_name member yet, set it here */
	if (!server->repl_set_name) {
		server->repl_set_name = strdup(set);
	}

	/* Check for flags */
	bson_find_field_as_bool(ptr, "ismaster", &ismaster);
	bson_find_field_as_bool(ptr, "arbiterOnly", &arbiter);

	/* Find all hosts */
	bson_find_field_as_array(ptr, "hosts", &hosts);
	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: set name: %s, ismaster: %d, is_arbiter: %d", set, ismaster, arbiter);
	*nr_hosts = 0;

	ptr = hosts;
	while (bson_array_find_next_string(&ptr, NULL, &string)) {
		(*nr_hosts)++;
		*found_hosts = realloc(*found_hosts, (*nr_hosts) * sizeof(char*));
		(*found_hosts)[*nr_hosts-1] = strdup(string);
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "found host: %s", string);
	}

	/* Set connection type depending on flags */
	if (ismaster) {
		con->connection_type = MONGO_NODE_PRIMARY;
	} else if (arbiter) {
		con->connection_type = MONGO_NODE_ARBITER;
	} else {
		con->connection_type = MONGO_NODE_SECONDARY;
	} /* TODO: case for mongos */

	free(data_buffer);

	con->last_ismaster = now.tv_sec;
	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: last ran at %ld", con->last_ismaster);

	return retval;
}
Exemplo n.º 4
0
static mongo_connection *mongo_get_connection_multiple(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message)
{
	mongo_connection *con = NULL;
	mongo_connection *tmp;
	mcon_collection  *collection = NULL;
	char             *con_error_message = NULL;
	mongo_read_preference tmp_rp; /* We only support NEAREST for MULTIPLE right now */
	int i;
	int found_connected_server = 0;
	mcon_str         *messages;

	mcon_str_ptr_init(messages);

	/* Create a connection to every of the servers in the seed list */
	for (i = 0; i < servers->count; i++) {
		tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message);

		if (tmp) {
			found_connected_server = 1;
		} else if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message);
			if (messages->l) {
				mcon_str_addl(messages, "; ", 2, 0);
			}
			mcon_str_add(messages, "Failed to connect to: ", 0);
			mcon_str_add(messages, servers->server[i]->host, 0);
			mcon_str_addl(messages, ":", 1, 0);
			mcon_str_add_int(messages, servers->server[i]->port);
			mcon_str_addl(messages, ": ", 2, 0);
			mcon_str_add(messages, con_error_message, 1); /* Also frees con_error_message */
		}
	}

	/* If we don't have a connected server then there is no point in continueing */
	if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) {
		return NULL;
	}

	/* When selecting a *mongos* node, readPreferences make no sense as we
	 * don't have a "primary" or "secondary" mongos. The mongos nodes aren't
	 * tagged either.  To pick a mongos we therefore simply pick the "nearest"
	 * mongos node. */
	tmp_rp.type = MONGO_RP_NEAREST;
	tmp_rp.tagsets = NULL;
	tmp_rp.tagset_count = 0;
	collection = mongo_find_candidate_servers(manager, &tmp_rp, servers);
	if (!collection || collection->count == 0) {
		if (messages->l) {
			*error_message = strdup(messages->d);
		} else {
			*error_message = strdup("No candidate servers found");
		}
		goto bailout;
	}
	collection = mongo_sort_servers(manager, collection, &servers->read_pref);
	collection = mongo_select_nearest_servers(manager, collection, &servers->read_pref);
	if (!collection) {
		*error_message = strdup("No server near us");
		goto bailout;
	}
	con = mongo_pick_server_from_set(manager, collection, &servers->read_pref);

bailout:
	/* Cleaning up */
	mcon_str_ptr_dtor(messages);
	if (collection) {
		mcon_collection_free(collection);	
	}
	return con;
}
/* Sends an ismaster command to the server and returns an array of new
 * connectable nodes
 *
 * Returns:
 * 0: when an error occurred
 * 1: when is master was run and worked
 * 2: when is master wasn't run due to the time-out limit
 * 3: when it all worked, but we need to remove the seed host (due to its name
 *    not being what the server thought it is) - in that case, the server in
 *    the last argument is changed
 * 4: when the call worked, but wasn't within our supported wire version range */
int mongo_connection_ismaster(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char **repl_set_name, int *nr_hosts, char ***found_hosts, char **error_message, mongo_server_def *server)
{
	mcon_str      *packet;
	char          *data_buffer;
	int32_t        max_bson_size = 0, max_message_size = 0, max_write_batch_size = 0;
	int32_t        min_wire_version = 0, max_wire_version = 0;
	char          *set = NULL;      /* For replicaset in return */
	char          *hosts, *passives = NULL, *ptr, *string;
	char          *msg; /* If set and its value is "isdbgrid", it signals we connected to a mongos */
	unsigned char  ismaster = 0, secondary = 0, arbiter = 0;
	char          *connected_name, *we_think_we_are;
	char          *tags;
	struct timeval now;
	int            retval = 1;

	gettimeofday(&now, NULL);
	if (((server ? con->last_replcheck : con->last_ismaster) + manager->ismaster_interval) > now.tv_sec) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: skipping: last ran at %ld, now: %ld, time left: %ld", con->last_ismaster, now.tv_sec, con->last_ismaster + manager->ismaster_interval - now.tv_sec);
		return 2;
	}

	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: start");
	packet = bson_create_ismaster_packet(con);

	if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) {
		return 0;
	}

	/* Find data fields */
	ptr = data_buffer + sizeof(int32_t); /* Skip the length */

	/* Find [min|max]WireVersion */
	if (bson_find_field_as_int32(ptr, "minWireVersion", &min_wire_version)) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: setting minWireVersion to %d", min_wire_version);
		con->min_wire_version = min_wire_version;
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: can't find minWireVersion, defaulting to %d", con->min_wire_version);
	}

	if (bson_find_field_as_int32(ptr, "maxWireVersion", &max_wire_version)) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: setting maxWireVersion to %d", max_wire_version);
		con->max_wire_version = max_wire_version;
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: can't find maxWireVersion, defaulting to %d", con->max_wire_version);
	}

	if (!manager->supports_wire_version(con->min_wire_version, con->max_wire_version, error_message)) {
		/* Error message set by supports_wire_version */
		free(data_buffer);
		return 4;
	}

	/* Find max bson size */
	if (bson_find_field_as_int32(ptr, "maxBsonObjectSize", &max_bson_size)) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: setting maxBsonObjectSize to %d", max_bson_size);
		con->max_bson_size = max_bson_size;
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: can't find maxBsonObjectSize, defaulting to %d", con->max_bson_size);
	}

	/* Find max message size */
	if (bson_find_field_as_int32(ptr, "maxMessageSizeBytes", &max_message_size)) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: setting maxMessageSizeBytes to %d", max_message_size);
		con->max_message_size = max_message_size;
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: can't find maxMessageSizeBytes, defaulting to %d", con->max_message_size);
	}

	/* Find max batch item size */
	if (bson_find_field_as_int32(ptr, "maxWriteBatchSize", &max_write_batch_size)) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: setting maxWriteBatchSize to %d", max_write_batch_size);
		con->max_write_batch_size = max_write_batch_size;
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: can't find maxWriteBatchSize, defaulting to %d", con->max_write_batch_size);
	}


	/* Check for flags */
	bson_find_field_as_bool(ptr, "ismaster", &ismaster);
	bson_find_field_as_bool(ptr, "secondary", &secondary);
	bson_find_field_as_bool(ptr, "arbiterOnly", &arbiter);
	bson_find_field_as_string(ptr, "setName", &set);

	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: set name: %s, ismaster: %d, secondary: %d, is_arbiter: %d", set, ismaster, secondary, arbiter);

	/* Set connection type depending on flags */
	if (ismaster) {
		/* Find msg and whether it contains "isdbgrid" */
		if (bson_find_field_as_string(ptr, "msg", (char**) &msg) && strcmp(msg, "isdbgrid") == 0) {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: msg contains 'isdbgrid' - we're connected to a mongos");
			con->connection_type = MONGO_NODE_MONGOS;
		} else if(set) {
			con->connection_type = MONGO_NODE_PRIMARY;
		} else {
			con->connection_type = MONGO_NODE_STANDALONE;
		}
	} else if (secondary) {
		con->connection_type = MONGO_NODE_SECONDARY;
	} else if (arbiter) {
		con->connection_type = MONGO_NODE_ARBITER;
	} else if (!set) {
		/* If there is no set, we assume this is an "old style" "slave" node */
		con->connection_type = MONGO_NODE_SECONDARY;
	} else {
		con->connection_type = MONGO_NODE_INVALID;
	}

	if (con->connection_type == MONGO_NODE_INVALID) {
		*error_message = strdup("ismaster: got unknown node type");
		free(data_buffer);
		return 0;
	}

	/* Find read preferences tags */
	con->tag_count = 0;
	con->tags = NULL;
	if (bson_find_field_as_document(ptr, "tags", (char**) &tags)) {
		char *it, *name, *value;
		int   length;

		it = tags;

		while (bson_array_find_next_string(&it, &name, &value)) {
			con->tags = realloc(con->tags, (con->tag_count + 1) * sizeof(char*));
			length = strlen(name) + strlen(value) + 2;
			con->tags[con->tag_count] = malloc(length);
			snprintf(con->tags[con->tag_count], length, "%s:%s", name, value);
			free(name);
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: added tag %s", con->tags[con->tag_count]);
			con->tag_count++;
		}
	}

	/* If we get passed in a server it means we want to validate this node against it, along with discovery ReplicaSet stuff */
	if (!server) {
		goto done;
	}

	/* We find out whether the machine we connected too, is actually the
	 * one we thought we were connecting too */
	/* MongoDB 1.8.x doesn't have the "me" field.
	 * The replicaset verification is done next step (setName). */
	if (bson_find_field_as_string(ptr, "me", &connected_name)) {
		we_think_we_are = mongo_server_hash_to_server(con->hash);
		if (strcmp(connected_name, we_think_we_are) == 0) {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the server name matches what we thought it'd be (%s).", we_think_we_are);
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the server name (%s) did not match with what we thought it'd be (%s).", connected_name, we_think_we_are);
			/* We reset the name as the server responded with a different name than
			 * what we thought it was */
			free(server->host);
			server->host = mcon_strndup(connected_name, strchr(connected_name, ':') - connected_name);
			server->port = atoi(strchr(connected_name, ':') + 1);
			retval = 3;
		}
		free(we_think_we_are);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Can't find 'me' in ismaster response, possibly not a replicaset (%s)", mongo_server_hash_to_server(con->hash));
	}

	/* Do replica set name test */
	if (!set) {
		char *errmsg = NULL;
		bson_find_field_as_string(ptr, "errmsg", &errmsg);
		if (errmsg) {
			*error_message = strdup(errmsg);
		} else {
			*error_message = strdup("Not a replicaset member");
		}
		free(data_buffer);
		return 0;
	} else if (*repl_set_name) {
		if (strcmp(set, *repl_set_name) != 0) {
			struct mcon_str *tmp;

			mcon_str_ptr_init(tmp);
			mcon_str_add(tmp, "Host does not match replicaset name. Expected: ", 0);
			mcon_str_add(tmp, *repl_set_name, 0);
			mcon_str_add(tmp, "; Found: ", 0);
			mcon_str_add(tmp, set, 0);

			*error_message = strdup(tmp->d);
			mcon_str_ptr_dtor(tmp);

			free(data_buffer);
			return 0;
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the found replicaset name matches the expected one (%s).", set);
		}
	} else if (*repl_set_name == NULL) {
		/* This can happen, in case the replicaset name was not given, but just
		 * bool(true) (or string("1")) in the connection options. */
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the replicaset name is not set, so we're using %s.", set);
		*repl_set_name = strdup(set);
	}

	/* If the server definition has not set the repl_set_name member yet, set it here */
	if (!server->repl_set_name) {
		server->repl_set_name = strdup(set);
	}

	/* Find all hosts */
	bson_find_field_as_array(ptr, "hosts", &hosts);
	bson_find_field_as_array(ptr, "passives", &passives);
	*nr_hosts = 0;

	/* Iterate over the "hosts" document */
	ptr = hosts;
	while (bson_array_find_next_string(&ptr, NULL, &string)) {
		(*nr_hosts)++;
		*found_hosts = realloc(*found_hosts, (*nr_hosts) * sizeof(char*));
		(*found_hosts)[*nr_hosts-1] = strdup(string);
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "found host: %s", string);
	}

	/* Iterate over the "passives" document (priority=0) */
	if (passives) {
		ptr = passives;
		while (bson_array_find_next_string(&ptr, NULL, &string)) {
			(*nr_hosts)++;
			*found_hosts = realloc(*found_hosts, (*nr_hosts) * sizeof(char*));
			(*found_hosts)[*nr_hosts-1] = strdup(string);
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "found host: %s (passive)", string);
		}
	}

	con->last_replcheck = now.tv_sec;

done:
	free(data_buffer);

	con->last_ismaster = now.tv_sec;
	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: last ran at %ld", con->last_ismaster);

	return retval;
}
Exemplo n.º 6
0
/* Sends an ismaster command to the server and returns an array of new
 * connectable nodes
 *
 * Returns:
 * 0: when an error occurred
 * 1: when is master was run and worked
 * 2: when is master wasn't run due to the time-out limit
 * 3: when it all worked, but we need to remove the seed host (due to its name
 *    not being what the server thought it is) - in that case, the server in
 *    the last argument is changed */
int mongo_connection_ismaster(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char **repl_set_name, int *nr_hosts, char ***found_hosts, char **error_message, mongo_server_def *server)
{
	mcon_str      *packet;
	char          *data_buffer;
	char          *set = NULL;      /* For replicaset in return */
	char          *hosts, *passives = NULL, *ptr, *string;
	char          *connected_name, *we_think_we_are;
	struct timeval now;
	int            retval = 1;

	gettimeofday(&now, NULL);
	if ((con->last_ismaster + manager->ismaster_interval) > now.tv_sec) {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: skipping: last ran at %ld, now: %ld, time left: %ld", con->last_ismaster, now.tv_sec, con->last_ismaster + manager->ismaster_interval - now.tv_sec);
		return 2;
	}

	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: start");
	packet = bson_create_ismaster_packet(con);

	if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) {
		return 0;
	}

	/* Find data fields */
	ptr = data_buffer + sizeof(int32_t); /* Skip the length */

	/* We find out whether the machine we connected too, is actually the
	 * one we thought we were connecting too */
	/* MongoDB 1.8.x doesn't have the "me" field.
	 * The replicaset verification is done next step (setName). */
	if (bson_find_field_as_string(ptr, "me", &connected_name)) {
		we_think_we_are = mongo_server_hash_to_server(con->hash);
		if (strcmp(connected_name, we_think_we_are) == 0) {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the server name matches what we thought it'd be (%s).", we_think_we_are);
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the server name (%s) did not match with what we thought it'd be (%s).", connected_name, we_think_we_are);
			/* We reset the name as the server responded with a different name than
			 * what we thought it was */
			free(server->host);
			server->host = mcon_strndup(connected_name, strchr(connected_name, ':') - connected_name);
			server->port = atoi(strchr(connected_name, ':') + 1);
			retval = 3;
		}
		free(we_think_we_are);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Can't find 'me' in ismaster response, possibly not a replicaset (%s)", mongo_server_hash_to_server(con->hash));
	}

	/* Do replica set name test */
	bson_find_field_as_string(ptr, "setName", &set);
	if (!set) {
		char *errmsg = NULL;
		bson_find_field_as_string(ptr, "errmsg", &errmsg);
		if (errmsg) {
			*error_message = strdup(errmsg);
		} else {
			*error_message = strdup("Not a replicaset member");
		}
		free(data_buffer);
		return 0;
	} else if (*repl_set_name) {
		if (strcmp(set, *repl_set_name) != 0) {
			struct mcon_str *tmp;

			mcon_str_ptr_init(tmp);
			mcon_str_add(tmp, "Host does not match replicaset name. Expected: ", 0);
			mcon_str_add(tmp, *repl_set_name, 0);
			mcon_str_add(tmp, "; Found: ", 0);
			mcon_str_add(tmp, set, 0);

			*error_message = strdup(tmp->d);
			mcon_str_ptr_dtor(tmp);

			free(data_buffer);
			return 0;
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the found replicaset name matches the expected one (%s).", set);
		}
	} else if (*repl_set_name == NULL) {
		/* This can happen, in case the replicaset name was not given, but just
		 * bool(true) (or string("1")) in the connection options. */
		mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the replicaset name is not set, so we're using %s.", set);
		*repl_set_name = strdup(set);
	}

	/* If the server definition has not set the repl_set_name member yet, set it here */
	if (!server->repl_set_name) {
		server->repl_set_name = strdup(set);
	}

	/* Find all hosts */
	bson_find_field_as_array(ptr, "hosts", &hosts);
	bson_find_field_as_array(ptr, "passives", &passives);
	*nr_hosts = 0;

	/* Iterate over the "hosts" document */
	ptr = hosts;
	while (bson_array_find_next_string(&ptr, NULL, &string)) {
		(*nr_hosts)++;
		*found_hosts = (char**) realloc(*found_hosts, (*nr_hosts) * sizeof(char*));
		(*found_hosts)[*nr_hosts-1] = strdup(string);
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "found host: %s", string);
	}

	/* Iterate over the "passives" document (priority=0) */
	if (passives) {
		ptr = passives;
		while (bson_array_find_next_string(&ptr, NULL, &string)) {
			(*nr_hosts)++;
			*found_hosts = (char**) realloc(*found_hosts, (*nr_hosts) * sizeof(char*));
			(*found_hosts)[*nr_hosts-1] = strdup(string);
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "found host: %s (passive)", string);
		}
	}

	free(data_buffer);

	con->last_ismaster = now.tv_sec;
	mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "ismaster: last ran at %ld", con->last_ismaster);

	return retval;
}