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); } }
/* 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); }
/* 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; } }
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 */
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; }
/* 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; }