{
/*
- * Insert the result of a query into the database
+ * Inserts the specified query into the dhttests.queries table
*
- * @param sqlqueryuid return value for the sql uid for this query
- * @param queryid gnunet internal query id (doesn't exist)
- * @param type the type of query (DHTLOG_GET, DHTLOG_PUT, DHTLOG_RESULT)
- * @param hops the hops the query has traveled
- * @param succeeded is successful or not (GNUNET_YES or GNUNET_NO)
- * @param GNUNET_PeerIdentity of the node the query is at now
- * @param key the GNUNET_HashCode of this query
+ * @param sqlqueruid inserted query uid
+ * @param queryid dht query id
+ * @param type type of the query
+ * @param hops number of hops query traveled
+ * @param succeeded whether or not query was successful
+ * @param node the node the query hit
+ * @param key the key of the query
*
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
*/
int (*insert_query) (unsigned long long *sqlqueryuid,
unsigned long long queryid, DHTLOG_MESSAGE_TYPES type,
const GNUNET_HashCode * key);
/*
- * Inserts the trial information into the database
+ * Inserts the specified trial into the dhttests.trials table
+ *
+ * @param trialuid return the trialuid of the newly inserted trial
+ * @param num_nodes how many nodes are in the trial
+ * @param topology integer representing topology for this trial
+ * @param blacklist_topology integer representing blacklist topology for this trial
+ * @param connect_topology integer representing connect topology for this trial
+ * @param connect_topology_option integer representing connect topology option
+ * @param connect_topology_option_modifier float to modify connect option
+ * @param topology_percentage percentage modifier for certain topologies
+ * @param topology_probability probability modifier for certain topologies
+ * @param puts number of puts to perform
+ * @param gets number of gets to perform
+ * @param concurrent number of concurrent requests
+ * @param settle_time time to wait between creating topology and starting testing
+ * @param num_rounds number of times to repeat the trial
+ * @param malicious_getters number of malicious GET peers in the trial
+ * @param malicious_putters number of malicious PUT peers in the trial
+ * @param malicious_droppers number of malicious DROP peers in the trial
+ * @param message string to put into DB for this trial
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
int (*insert_trial) (unsigned long long *trialuid, int num_nodes, int topology,
int blacklist_topology, int connect_topology,
char *message);
/*
- * Update the trial information with the ending time and dropped message stats
+ * Update dhttests.trials table with current server time as end time
+ *
+ * @param trialuid trial to update
+ * @param totalMessagesDropped stats value for messages dropped
+ * @param totalBytesDropped stats value for total bytes dropped
+ * @param unknownPeers stats value for unknown peers
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
*/
int (*update_trial) (unsigned long long trialuid,
unsigned long long totalMessagesDropped,
unsigned long long unknownPeers);
/*
- * Update the trial information with the total connections
+ * Records the current topology (number of connections, time, trial)
+ *
+ * @param num_connections how many connections are in the topology
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+ int (*insert_topology) (int num_connections);
+
+ /*
+ * Records a connection between two peers in the current topology
+ *
+ * @param first one side of the connection
+ * @param second other side of the connection
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+ int (*insert_extended_topology) (struct GNUNET_PeerIdentity *first, struct GNUNET_PeerIdentity *second);
+
+ /*
+ * Update dhttests.trials table with total connections information
+ *
+ * @param trialuid the trialuid to update
+ * @param totalConnections the number of connections
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
*/
int (*update_connections) (unsigned long long trialuid,
unsigned int totalConnections);
/*
- * Insert the query information from a single hop into the database
- *
- * @param sqlqueryuid return value for the sql uid for this query
- * @param queryid gnunet internal query id (doesn't exist)
- * @param type the type of query (DHTLOG_GET, DHTLOG_PUT, DHTLOG_RESULT)
- * @param hops the hops the query has traveled
- * @param succeeded query is successful or not (GNUNET_YES or GNUNET_NO)
- * @param node GNUNET_PeerIdentity of the node the query is at now
- * @param key the GNUNET_HashCode of this query
- * @param from_node GNUNET_PeerIdentity of the node the query was
- * received from (NULL if origin)
- * @param to_node GNUNET_PeerIdentity of the node this node will forward
- * to (NULL if none)
+ * Update dhttests.trials table with total connections information
+ *
+ * @param connections the number of connections
*
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
+ */
+ int (*update_topology) (unsigned int connections);
+
+ /*
+ * Inserts the specified route information into the dhttests.routes table
+ *
+ * @param sqlqueruid inserted query uid
+ * @param queryid dht query id
+ * @param type type of the query
+ * @param hops number of hops query traveled
+ * @param succeeded whether or not query was successful
+ * @param node the node the query hit
+ * @param key the key of the query
+ * @param from_node the node that sent the message to node
+ * @param to_node next node to forward message to
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
*/
int (*insert_route) (unsigned long long *sqlqueryuid,
unsigned long long queryid,
/*
* Inserts the specified node into the dhttests.nodes table
+ *
+ * @param nodeuid the inserted node uid
+ * @param node the node to insert
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
int (*insert_node) (unsigned long long *nodeuid,
struct GNUNET_PeerIdentity * node);
/*
- * Inserts a dhtkey into the database
+ * Inserts the specified dhtkey into the dhttests.dhtkeys table,
+ * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
+ *
+ * @param dhtkeyuid return value
+ * @param dhtkey hashcode of key to insert
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
int (*insert_dhtkey) (unsigned long long *dhtkeyuid,
const GNUNET_HashCode * dhtkey);
return GNUNET_OK;
}
+
+/*
+ * Records the current topology (number of connections, time, trial)
+ *
+ * @param num_connections how many connections are in the topology
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+add_topology (int num_connections)
+{
+ return GNUNET_OK;
+}
+
+/*
+ * Records a connection between two peers in the current topology
+ *
+ * @param first one side of the connection
+ * @param second other side of the connection
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+add_extended_topology (struct GNUNET_PeerIdentity *first, struct GNUNET_PeerIdentity *second)
+{
+ return GNUNET_OK;
+}
+
+/*
+ * Update dhttests.topology table with total connections information
+ *
+ * @param totalConnections the number of connections
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
+ */
+int
+update_topology (unsigned int connections)
+{
+ return GNUNET_OK;
+}
+
+
/*
* Provides the dhtlog api
*
plugin->dhtlog_api->insert_node = &add_node;
plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
plugin->dhtlog_api->update_connections = &add_connections;
+ plugin->dhtlog_api->insert_topology = &add_topology;
+ plugin->dhtlog_api->update_topology = &update_topology;
+ plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
return NULL;
}
#define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
static struct StatementHandle *get_trial;
+#define GET_TOPOLOGY_STMT "SELECT MAX( topology_uid ) FROM topology"
+static struct StatementHandle *get_topology;
+
#define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
static struct StatementHandle *get_dhtkeyuid;
#define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
static struct StatementHandle *get_nodeuid;
+#define INSERT_TOPOLOGY_STMT "INSERT INTO topology (trialuid, date, connections) "\
+ "VALUES (?, NOW(), ?)"
+static struct StatementHandle *insert_topology;
+
+#define EXTEND_TOPOLOGY_STMT "INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
+ "VALUES (?, ?, ?)"
+static struct StatementHandle *extend_topology;
+
+#define UPDATE_TOPOLOGY_STMT "update topology set connections = ? where topology_uid = ?"
+static struct StatementHandle *update_topology;
+
/**
* Run a query (not a select statement)
*
") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
return GNUNET_SYSERR;
+ if (MRUNS ("CREATE TABLE IF NOT EXISTS `topology` ("
+ "`topology_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ "`trialuid` int(10) unsigned NOT NULL,"
+ "`date` datetime NOT NULL,"
+ "`connections` int(10) unsigned NOT NULL,"
+ "PRIMARY KEY (`topology_uid`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
+ return GNUNET_SYSERR;
+
+ if (MRUNS ("CREATE TABLE IF NOT EXISTS `extended_topology` ("
+ "`extended_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ "`topology_uid` int(10) unsigned NOT NULL,"
+ "`uid_first` int(10) unsigned NOT NULL,"
+ "`uid_second` int(10) unsigned NOT NULL,"
+ "PRIMARY KEY (`extended_uid`)"
+ ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
+ return GNUNET_SYSERR;
+
if (MRUNS ("SET AUTOCOMMIT = 1"))
return GNUNET_SYSERR;
PINIT (get_dhtkeyuid, GET_DHTKEYUID_STMT) ||
PINIT (get_nodeuid, GET_NODEUID_STMT) ||
PINIT (update_connection, UPDATE_CONNECTIONS_STMT) ||
- PINIT (get_trial, GET_TRIAL_STMT))
+ PINIT (get_trial, GET_TRIAL_STMT) ||
+ PINIT (get_topology, GET_TOPOLOGY_STMT) ||
+ PINIT (insert_topology, INSERT_TOPOLOGY_STMT)||
+ PINIT (update_topology, UPDATE_TOPOLOGY_STMT)||
+ PINIT (extend_topology, EXTEND_TOPOLOGY_STMT))
{
return GNUNET_SYSERR;
}
return total;
}
+
+static int
+get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
+{
+ MYSQL_BIND rbind[1];
+ struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
+ unsigned long long p_len;
+
+ int ret;
+ memset (rbind, 0, sizeof (rbind));
+ rbind[0].buffer_type = MYSQL_TYPE_LONG;
+ rbind[0].buffer = nodeuid;
+ rbind[0].is_unsigned = GNUNET_YES;
+
+ GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
+ p_len = strlen ((char *) &encPeer);
+
+ if (1 != (ret = prepared_statement_run_select (get_nodeuid,
+ 1,
+ rbind,
+ return_ok,
+ NULL,
+ MYSQL_TYPE_LONG,
+ ¤t_trial,
+ GNUNET_YES,
+ MYSQL_TYPE_VAR_STRING,
+ &encPeer,
+ max_varchar_len,
+ &p_len, -1)))
+ {
+#if DEBUG_DHTLOG
+ fprintf (stderr, "FAILED\n");
+#endif
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
static int
get_current_trial (unsigned long long *trialuid)
{
return GNUNET_OK;
}
+static int
+get_current_topology (unsigned long long *topologyuid)
+{
+ MYSQL_BIND rbind[1];
+
+ memset (rbind, 0, sizeof (rbind));
+ rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+ rbind[0].is_unsigned = 1;
+ rbind[0].buffer = topologyuid;
+
+ if ((GNUNET_OK !=
+ prepared_statement_run_select (get_topology,
+ 1,
+ rbind,
+ return_ok, NULL, -1)))
+ {
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
+
+static int
+get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
+{
+ MYSQL_BIND rbind[1];
+ struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
+ unsigned long long k_len;
+ memset (rbind, 0, sizeof (rbind));
+ rbind[0].buffer_type = MYSQL_TYPE_LONG;
+ rbind[0].is_unsigned = 1;
+ rbind[0].buffer = dhtkeyuid;
+ GNUNET_CRYPTO_hash_to_enc (key, &encKey);
+ k_len = strlen ((char *) &encKey);
+
+ if ((GNUNET_OK !=
+ prepared_statement_run_select (get_dhtkeyuid,
+ 1,
+ rbind,
+ return_ok, NULL,
+ MYSQL_TYPE_VAR_STRING,
+ &encKey,
+ max_varchar_len,
+ &k_len,
+ MYSQL_TYPE_LONGLONG,
+ ¤t_trial,
+ GNUNET_YES, -1)))
+ {
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
/**
* Run a prepared statement that does NOT produce results.
return GNUNET_OK;
}
-static int
-get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
-{
- MYSQL_BIND rbind[1];
- struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
- unsigned long long k_len;
- memset (rbind, 0, sizeof (rbind));
- rbind[0].buffer_type = MYSQL_TYPE_LONG;
- rbind[0].is_unsigned = 1;
- rbind[0].buffer = dhtkeyuid;
- GNUNET_CRYPTO_hash_to_enc (key, &encKey);
- k_len = strlen ((char *) &encKey);
-
- if ((GNUNET_OK !=
- prepared_statement_run_select (get_dhtkeyuid,
- 1,
- rbind,
- return_ok, NULL,
- MYSQL_TYPE_VAR_STRING,
- &encKey,
- max_varchar_len,
- &k_len,
- MYSQL_TYPE_LONGLONG,
- ¤t_trial,
- GNUNET_YES, -1)))
- {
- return GNUNET_SYSERR;
- }
-
- return GNUNET_OK;
-}
-
/*
* Inserts the specified dhtkey into the dhttests.dhtkeys table,
* stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
}
-static int
-get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
-{
- MYSQL_BIND rbind[1];
- struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
- unsigned long long p_len;
-
- int ret;
- memset (rbind, 0, sizeof (rbind));
- rbind[0].buffer_type = MYSQL_TYPE_LONG;
- rbind[0].buffer = nodeuid;
- rbind[0].is_unsigned = GNUNET_YES;
-
- GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
- p_len = strlen ((char *) &encPeer);
-
- if (1 != (ret = prepared_statement_run_select (get_nodeuid,
- 1,
- rbind,
- return_ok,
- NULL,
- MYSQL_TYPE_LONG,
- ¤t_trial,
- GNUNET_YES,
- MYSQL_TYPE_VAR_STRING,
- &encPeer,
- max_varchar_len,
- &p_len, -1)))
- {
-#if DEBUG_DHTLOG
- fprintf (stderr, "FAILED\n");
-#endif
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
/*
* Inserts the specified node into the dhttests.nodes table
return GNUNET_SYSERR;
}
+/*
+ * Update dhttests.topology table with total connections information
+ *
+ * @param totalConnections the number of connections
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
+ */
+int
+update_current_topology (unsigned int connections)
+{
+ int ret;
+ unsigned long long topologyuid;
+
+ get_current_topology(&topologyuid);
+
+ if (GNUNET_OK !=
+ (ret = prepared_statement_run (update_topology,
+ NULL,
+ MYSQL_TYPE_LONG,
+ &connections,
+ GNUNET_YES,
+ MYSQL_TYPE_LONGLONG,
+ &topologyuid, GNUNET_YES, -1)))
+ {
+ if (ret == GNUNET_SYSERR)
+ {
+ return GNUNET_SYSERR;
+ }
+ }
+ if (ret > 0)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+/*
+ * Records the current topology (number of connections, time, trial)
+ *
+ * @param num_connections how many connections are in the topology
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+add_topology (int num_connections)
+{
+ int ret;
+
+ if (GNUNET_OK !=
+ (ret = prepared_statement_run (insert_topology,
+ NULL,
+ MYSQL_TYPE_LONGLONG,
+ ¤t_trial,
+ GNUNET_YES,
+ MYSQL_TYPE_LONG,
+ &num_connections,
+ GNUNET_YES, -1)))
+ {
+ if (ret == GNUNET_SYSERR)
+ {
+ return GNUNET_SYSERR;
+ }
+ }
+ if (ret > 0)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+/*
+ * Records a connection between two peers in the current topology
+ *
+ * @param first one side of the connection
+ * @param second other side of the connection
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+add_extended_topology (struct GNUNET_PeerIdentity *first, struct GNUNET_PeerIdentity *second)
+{
+ int ret;
+ unsigned long long first_uid;
+ unsigned long long second_uid;
+ unsigned long long topologyuid;
+
+ if (GNUNET_OK != get_current_topology(&topologyuid))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != get_node_uid(&first_uid, &first->hashPubKey))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != get_node_uid(&second_uid, &second->hashPubKey))
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK !=
+ (ret = prepared_statement_run (extend_topology,
+ NULL,
+ MYSQL_TYPE_LONGLONG,
+ &topologyuid,
+ GNUNET_YES,
+ MYSQL_TYPE_LONGLONG,
+ &first_uid,
+ GNUNET_YES,
+ MYSQL_TYPE_LONGLONG,
+ &second_uid,
+ GNUNET_YES,-1)))
+ {
+ if (ret == GNUNET_SYSERR)
+ {
+ return GNUNET_SYSERR;
+ }
+ }
+ if (ret > 0)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
/*
* Provides the dhtlog api
*
plugin->dhtlog_api->insert_node = &add_node;
plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
plugin->dhtlog_api->update_connections = &add_connections;
+ plugin->dhtlog_api->insert_topology = &add_topology;
+ plugin->dhtlog_api->update_topology = &update_current_topology;
+ plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
get_current_trial (¤t_trial);
return NULL;
prepared_statement_close(get_nodeuid);
prepared_statement_close(update_connection);
prepared_statement_close(get_trial);
+ prepared_statement_close(get_topology);
+ prepared_statement_close(insert_topology);
+ prepared_statement_close(update_topology);
+ prepared_statement_close(extend_topology);
if (conn != NULL)
mysql_close (conn);
#define EXTEND_TOPOLOGY_STMT "prepare extend_topology from 'INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
"VALUES (@temp_topology, ?, ?)'"
+#define UPDATE_TOPOLOGY_STMT "prepare update_topology from 'update topology set connections = ? where topology_uid = @temp_topology'"
#define INSERT_TRIALS_STMT "prepare insert_trial from 'INSERT INTO trials"\
"(starttime, numnodes, topology,"\
PINIT (GET_DHTKEYUID_STMT) ||
PINIT (GET_NODEUID_STMT) ||
PINIT (UPDATE_CONNECTIONS_STMT) ||
- PINIT (GET_TRIAL_STMT))
+ PINIT (INSERT_TOPOLOGY_STMT) ||
+ PINIT (EXTEND_TOPOLOGY_STMT) ||
+ PINIT (UPDATE_TOPOLOGY_STMT) ||
+ PINIT (GET_TRIAL_STMT) ||
+ PINIT (GET_TOPOLOGY_STMT))
{
return GNUNET_SYSERR;
}
return GNUNET_SYSERR;
}
+
+/*
+ * Update dhttests.topology table with total connections information
+ *
+ * @param totalConnections the number of connections
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
+ */
+int
+update_topology (unsigned int connections)
+{
+ int ret;
+ if (outfile == NULL)
+ return GNUNET_SYSERR;
+
+ ret = fprintf(outfile, "set @temp_conns = %u;\n", connections);
+
+ if (ret < 0)
+ return GNUNET_SYSERR;
+
+ ret = fprintf(outfile, "execute update_topology using @temp_conns;\n");
+
+ if (ret >= 0)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+}
+
/*
* Inserts the specified query into the dhttests.queries table
*
plugin->dhtlog_api->insert_node = &add_node;
plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
plugin->dhtlog_api->update_connections = &add_connections;
+ plugin->dhtlog_api->insert_topology = &add_topology;
+ plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
+ plugin->dhtlog_api->update_topology = &update_topology;
return NULL;
}
static int ok;
+#define CHECK(a) if (a != GNUNET_OK) return a
/**
* Actual test of the service operations
*/
.75, .25, .5, 42, 14,
5, 1, 12, 0, 0, 0,
"TEST INSERT TRIAL");
+ CHECK(ret);
- if (ret != GNUNET_OK)
- {
- return ret;
- }
+ ret = api->insert_topology(500);
+
+ CHECK(ret);
fprintf (stderr, "Trial uid is %llu\n", trialuid);
ret = api->insert_node (&nodeuid, &p3);
ret = api->insert_node (&nodeuid, &p4);
- if (ret != GNUNET_OK)
- {
- fprintf (stderr, "received ret value of %d\n", ret);
- return ret;
- }
+ CHECK(ret);
+ ret = api->insert_topology(0);
+ ret = api->insert_extended_topology(&p1, &p2);
+ ret = api->insert_extended_topology(&p3, &p4);
+ ret = api->update_topology(101);
+ CHECK(ret);
ret = api->insert_dhtkey (&dhtkeyuid, &k1);
ret = api->insert_dhtkey (&dhtkeyuid, &k2);
- if (ret != GNUNET_OK)
- {
- fprintf (stderr, "received ret value of %d\n", ret);
- return ret;
- }
+ CHECK(ret);
ret = api->insert_query (&sqlqueryuid, internaluid, 2, 4, 0, &p2, &k1);
fprintf (stderr, "Updating trial %llu with endtime of now\n", trialuid);
ret = api->update_trial (trialuid, 0, 0, 0);
- if (ret != GNUNET_OK)
- {
- return ret;
- }
+ CHECK(ret);
return 0;
}