Example #1
0
static PHP_INI_MH(OnUpdateIsMasterInterval)
{
	long converted_val;

	if (new_value && is_numeric_string(new_value, new_value_length, &converted_val, NULL, 0) == IS_LONG && converted_val > 0) {
		MonGlo(manager)->ismaster_interval = converted_val;
		return SUCCESS;
	}

	return FAILURE;
}
void php_mongo_kill_cursor(mongo_connection *con, int64_t cursor_id TSRMLS_DC)
{
    char quickbuf[128];
    mongo_buffer buf;
    char *error_message;

    buf.pos = quickbuf;
    buf.start = buf.pos;
    buf.end = buf.start + 128;

    mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_WARN, "Killing unfinished cursor %ld", cursor_id);

    php_mongo_write_kill_cursors(&buf, cursor_id, MONGO_DEFAULT_MAX_MESSAGE_SIZE TSRMLS_CC);
#if MONGO_PHP_STREAMS
    mongo_log_stream_killcursor(con, cursor_id TSRMLS_CC);
#endif

    if (MonGlo(manager)->send(con, NULL, buf.start, buf.pos - buf.start, (char**) &error_message) == -1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't kill cursor %lld: %s", (long long int) cursor_id, error_message);
        free(error_message);
    }
}
Example #3
0
/* Timestamp is 4 bytes of seconds since epoch and 4 bytes of increment. */
PHP_METHOD(MongoTimestamp, __construct)
{
    long sec = 0, inc = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &sec, &inc) == FAILURE) {
        return;
    }

    if (ZEND_NUM_ARGS() == 0) {
        sec = time(0);
    }
    if (ZEND_NUM_ARGS() <= 1 && !inc) {
        inc = MonGlo(ts_inc)++;
    }

    zend_update_property_long(mongo_ce_Timestamp, getThis(), "sec", strlen("sec"), sec TSRMLS_CC);
    zend_update_property_long(mongo_ce_Timestamp, getThis(), "inc", strlen("inc"), inc TSRMLS_CC);
}
Example #4
0
/* Allocates and bootstraps a new php_mongo_batch and its mongo_buffer.
 * The buffer is ready for adding documents as the wire header and command start has been written */
void php_mongo_api_batch_make(mongo_write_batch_object *intern, char *dbname, char *collectionname, php_mongo_write_types type TSRMLS_DC) /* {{{ */
{
	php_mongo_batch *batch = ecalloc(1, sizeof(php_mongo_batch));
	char *cmd_ns;

	CREATE_BUF(batch->buffer, INITIAL_BUF_SIZE);
	batch->request_id = MonGlo(request_id);

	spprintf(&cmd_ns, 0, "%s.$cmd", dbname);
	batch->container_pos = php_mongo_api_write_header(&batch->buffer, cmd_ns TSRMLS_CC);
	batch->batch_pos     = php_mongo_api_write_start(&batch->buffer, type, collectionname TSRMLS_CC);
	efree(cmd_ns);

	if (intern->batch) {
		intern->batch->next = batch;
		batch->first = intern->batch->first;
		intern->batch = batch;
	} else {
		intern->batch = batch;
		batch->first = intern->batch;
	}
}
Example #5
0
zend_object_handlers mongo_id_handlers;

void generate_id(char *data TSRMLS_DC)
{
	int inc;

#ifdef WIN32
	int pid = GetCurrentThreadId();
#else
	int pid = (int)getpid();
#endif

	unsigned t = (unsigned) time(0);
	char *T = (char*)&t,
	*M = (char*)&MonGlo(machine),
	*P = (char*)&pid,
	*I = (char*)&inc;

	/* inc */
	inc = MonGlo(inc);
	MonGlo(inc)++;

	/* actually generate the MongoId */
#if PHP_C_BIGENDIAN
	/* 4 bytes ts */
	memcpy(data, T, 4);

	/* we add 1 or 2 to the pointers so we don't end up with all 0s, as the
	 * interesting stuff is at the end for big endian systems */
Example #6
0
void* php_mongo_io_stream_connect(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, char **error_message)
{
	char *errmsg;
	int errcode;
	php_stream *stream;
	char *hash = mongo_server_create_hash(server);
	struct timeval ctimeout = {0, 0};
	char *dsn;
	int dsn_len;
	int tcp_socket = 1;
	ERROR_HANDLER_DECLARATION(error_handler)

	TSRMLS_FETCH();

	if (server->host[0] == '/') {
		dsn_len = spprintf(&dsn, 0, "unix://%s", server->host);
		tcp_socket = 0;
	} else {
		dsn_len = spprintf(&dsn, 0, "tcp://%s:%d", server->host, server->port);
	}


	/* Connection timeout behavior varies based on the following:
	 * - Negative => no timeout (i.e. block indefinitely)
	 * - Zero => not specified (PHP will use default_socket_timeout)
	 * - Positive => used specified timeout */
	if (options->connectTimeoutMS) {
		/* Convert negative value to -1 second, which implies no timeout */
		int connectTimeoutMS = options->connectTimeoutMS < 0 ? -1000 : options->connectTimeoutMS;

		ctimeout.tv_sec = connectTimeoutMS / 1000;
		ctimeout.tv_usec = (connectTimeoutMS % 1000) * 1000;
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) with connection timeout: %d.%06d", dsn, hash, ctimeout.tv_sec, ctimeout.tv_usec);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) without connection timeout (default_socket_timeout will be used)", dsn, hash);
	}

	ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
	stream = php_stream_xport_create(dsn, dsn_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hash, options->connectTimeoutMS > 0 ? &ctimeout : NULL, (php_stream_context *)options->ctx, &errmsg, &errcode);
	ERROR_HANDLER_RESTORE(error_handler);

	efree(dsn);
	free(hash);

	if (!stream) {
		/* error_message will be free()d, but errmsg was allocated by PHP and needs efree() */
		*error_message = strdup(errmsg);
		efree(errmsg);
		return NULL;
	}

	if (tcp_socket) {
		int socket = ((php_netstream_data_t*)stream->abstract)->socket;
		int flag = 1;

		setsockopt(socket, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
	}

	if (options->ssl) {
		int crypto_enabled;

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);

		if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0) {
			ERROR_HANDLER_RESTORE(error_handler);
			*error_message = strdup("Cannot setup SSL, is ext/openssl loaded?");
			php_stream_close(stream);
			return NULL;
		}

		crypto_enabled = php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC);
		ERROR_HANDLER_RESTORE(error_handler);

		if (crypto_enabled < 0) {
			/* Setting up crypto failed. Thats only OK if we only preferred it */
			if (options->ssl == MONGO_SSL_PREFER) {
				/* FIXME: We can't actually get here because we reject setting
				 * this option to prefer in mcon/parse.c. This is however
				 * probably what we need to do in the future when mongod starts
				 * actually supporting this! :) */
				mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Failed establishing SSL for %s:%d", server->host, server->port);
				php_stream_xport_crypto_enable(stream, 0 TSRMLS_CC);
			} else {
				*error_message = strdup("Can't connect over SSL, is mongod running with SSL?");
				php_stream_close(stream);
				return NULL;
			}
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Establish SSL for %s:%d", server->host, server->port);
		}
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Not establishing SSL for %s:%d", server->host, server->port);
	}

	/* Socket timeout behavior uses the same logic as connectTimeoutMS */
	if (options->socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		/* Convert negative value to -1 second, which implies no timeout */
		int socketTimeoutMS = options->socketTimeoutMS < 0 ? -1000 : options->socketTimeoutMS;

		rtimeout.tv_sec = socketTimeoutMS / 1000;
		rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;
		php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Setting stream timeout to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	}


	/* Avoid a weird leak warning in debug mode when freeing the stream */
#if ZEND_DEBUG
	stream->__exposed = 1;
#endif

	return stream;

}
Example #7
0
/* Returns the bytes read on success
 * Returns -31 on unknown failure
 * Returns -80 on timeout
 * Returns -32 when remote server closes the connection
 */
int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *options, int timeout, void *data, int size, char **error_message)
{
	int num = 1, received = 0;
	TSRMLS_FETCH();

	int socketTimeoutMS = options->socketTimeoutMS ? options->socketTimeoutMS : FG(default_socket_timeout) * 1000;

	/* Convert negative values to -1 second, which implies no timeout */
	socketTimeoutMS = socketTimeoutMS < 0 ? -1000 : socketTimeoutMS;
	timeout = timeout < 0 ? -1000 : timeout;

	/* Socket timeout behavior varies based on the following:
	 * - Negative => no timeout (i.e. block indefinitely)
	 * - Zero => not specified (no changes to existing configuration)
	 * - Positive => used specified timeout (revert to previous value later) */
	if (timeout && timeout != socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		rtimeout.tv_sec = timeout / 1000;
		rtimeout.tv_usec = (timeout % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Setting the stream timeout to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	} else {
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "No timeout changes for %s", con->hash);
	}

	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_READ, 0, size TSRMLS_CC);

	/* this can return FAILED if there is just no more data from db */
	while (received < size && num > 0) {
		int len = 4096 < (size - received) ? 4096 : size - received;
		ERROR_HANDLER_DECLARATION(error_handler)

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
		num = php_stream_read(con->socket, (char *) data, len);
		ERROR_HANDLER_RESTORE(error_handler);

		if (num < 0) {
			/* Doesn't look like this can happen, php_sockop_read overwrites
			 * the failure from recv() to return 0 */
			*error_message = strdup("Read from socket failed");
			return -31;
		}

		/* It *may* have failed. It also may simply have no data */
		if (num == 0) {
			zval *metadata;

			MAKE_STD_ZVAL(metadata);
			array_init(metadata);
			if (php_stream_populate_meta_data(con->socket, metadata)) {
				zval **tmp;

				if (zend_hash_find(Z_ARRVAL_P(metadata), "timed_out", sizeof("timed_out"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						struct timeval rtimeout = {0, 0};

						if (timeout > 0 && options->socketTimeoutMS != timeout) {
							rtimeout.tv_sec = timeout / 1000;
							rtimeout.tv_usec = (timeout % 1000) * 1000;
						} else {
							/* Convert timeout=-1 to -1second, which PHP interprets as no timeout */
							int socketTimeoutMS = options->socketTimeoutMS == -1 ? -1000 : options->socketTimeoutMS;
							rtimeout.tv_sec = socketTimeoutMS / 1000;
							rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;
						}
						*error_message = malloc(256);
						snprintf(*error_message, 256, "Read timed out after reading %d bytes, waited for %d.%06d seconds", num, rtimeout.tv_sec, rtimeout.tv_usec);
						zval_ptr_dtor(&metadata);
						return -80;
					}
				}
				if (zend_hash_find(Z_ARRVAL_P(metadata), "eof", sizeof("eof"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						*error_message = strdup("Remote server has closed the connection");
						zval_ptr_dtor(&metadata);
						return -32;
					}
				}
			}
			zval_ptr_dtor(&metadata);
		}

		data = (char*)data + num;
		received += num;
	}
	/* PHP may have sent notify-progress of *more then* 'received' in some
	 * cases.
	 * PHP will read 8192 byte chunks at a time, but if we request less data
	 * then that PHP will just buffer the rest, which is fine.  It could
	 * confuse users a little, why their progress update was higher then the
	 * max-bytes-expected though... */
	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_COMPLETED, received, size TSRMLS_CC);

	/* If the timeout was changed, revert to the previous value now */
	if (timeout && timeout != socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		/* If socketTimeoutMS was never specified, revert to default_socket_timeout */
		if (options->socketTimeoutMS == 0) {
			mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Stream timeout will be reverted to default_socket_timeout (%d)", FG(default_socket_timeout));
		}

		rtimeout.tv_sec = socketTimeoutMS / 1000;
		rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Now setting stream timeout back to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	}

	return received;
}