char * jsonrpc_msg_from_json(struct json *json, struct jsonrpc_msg **msgp) { struct json *method = NULL; struct jsonrpc_msg *msg = NULL; struct shash *object; char *error; if (json->type != JSON_OBJECT) { error = xstrdup("message is not a JSON object"); goto exit; } object = json_object(json); method = shash_find_and_delete(object, "method"); if (method && method->type != JSON_STRING) { error = xstrdup("method is not a JSON string"); goto exit; } msg = xzalloc(sizeof *msg); msg->method = method ? xstrdup(method->u.string) : NULL; msg->params = null_from_json_null(shash_find_and_delete(object, "params")); msg->result = null_from_json_null(shash_find_and_delete(object, "result")); msg->error = null_from_json_null(shash_find_and_delete(object, "error")); msg->id = null_from_json_null(shash_find_and_delete(object, "id")); msg->type = (msg->result ? JSONRPC_REPLY : msg->error ? JSONRPC_ERROR : msg->id ? JSONRPC_REQUEST : JSONRPC_NOTIFY); if (!shash_is_empty(object)) { error = xasprintf("message has unexpected member \"%s\"", shash_first(object)->name); goto exit; } error = jsonrpc_msg_is_valid(msg); if (error) { goto exit; } exit: json_destroy(method); json_destroy(json); if (error) { jsonrpc_msg_destroy(msg); msg = NULL; } *msgp = msg; return error; }
void replication_run(void) { if (!session) { return; } jsonrpc_session_run(session); for (int i = 0; jsonrpc_session_is_connected(session) && i < 50; i++) { struct jsonrpc_msg *msg; unsigned int seqno; seqno = jsonrpc_session_get_seqno(session); if (seqno != session_seqno || state == RPL_S_INIT) { session_seqno = seqno; request_ids_clear(); struct jsonrpc_msg *request; request = jsonrpc_create_request("list_dbs", json_array_create_empty(), NULL); request_ids_add(request->id, NULL); jsonrpc_session_send(session, request); replication_dbs_destroy(); replication_dbs = replication_db_clone(&local_dbs); state = RPL_S_DB_REQUESTED; VLOG_DBG("Send list_dbs request"); } msg = jsonrpc_session_recv(session); if (!msg) { continue; } if (msg->type == JSONRPC_NOTIFY && state != RPL_S_ERR && !strcmp(msg->method, "update")) { if (msg->params->type == JSON_ARRAY && msg->params->u.array.n == 2 && msg->params->u.array.elems[0]->type == JSON_STRING) { char *db_name = msg->params->u.array.elems[0]->u.string; struct ovsdb *db = find_db(db_name); if (db) { struct ovsdb_error *error; error = process_notification(msg->params->u.array.elems[1], db); if (error) { ovsdb_error_assert(error); state = RPL_S_ERR; } } } } else if (msg->type == JSONRPC_REPLY) { struct ovsdb *db; if (!request_ids_lookup_and_free(msg->id, &db)) { VLOG_WARN("received unexpected reply"); goto next; } switch (state) { case RPL_S_DB_REQUESTED: if (msg->result->type != JSON_ARRAY) { struct ovsdb_error *error; error = ovsdb_error("list-dbs failed", "list_dbs response is not array"); ovsdb_error_assert(error); state = RPL_S_ERR; } else { size_t i; for (i = 0; i < msg->result->u.array.n; i++) { const struct json *name = msg->result->u.array.elems[i]; if (name->type == JSON_STRING) { /* Send one schema request for each remote DB. */ const char *db_name = json_string(name); struct ovsdb *db = find_db(db_name); if (db) { struct jsonrpc_msg *request = jsonrpc_create_request( "get_schema", json_array_create_1( json_string_create(db_name)), NULL); request_ids_add(request->id, db); jsonrpc_session_send(session, request); } } } VLOG_DBG("Send schema requests"); state = RPL_S_SCHEMA_REQUESTED; } break; case RPL_S_SCHEMA_REQUESTED: { struct ovsdb_schema *schema; struct ovsdb_error *error; error = ovsdb_schema_from_json(msg->result, &schema); if (error) { ovsdb_error_assert(error); state = RPL_S_ERR; } if (db != find_db(schema->name)) { /* Unexpected schema. */ VLOG_WARN("unexpected schema %s", schema->name); state = RPL_S_ERR; } else if (!ovsdb_schema_equal(schema, db->schema)) { /* Schmea version mismatch. */ VLOG_INFO("Schema version mismatch, %s not replicated", schema->name); shash_find_and_delete(replication_dbs, schema->name); } ovsdb_schema_destroy(schema); /* After receiving schemas, reset the local databases that * will be monitored and send out monitor requests for them. */ if (hmap_is_empty(&request_ids)) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, replication_dbs) { db = node->data; struct ovsdb_error *error = reset_database(db); if (error) { const char *db_name = db->schema->name; shash_find_and_delete(replication_dbs, db_name); ovsdb_error_assert(error); VLOG_WARN("Failed to reset database, " "%s not replicated.", db_name); } } if (shash_is_empty(replication_dbs)) { VLOG_WARN("Nothing to replicate."); state = RPL_S_ERR; } else { SHASH_FOR_EACH (node, replication_dbs) { db = node->data; struct ovsdb *db = node->data; struct jsonrpc_msg *request = create_monitor_request(db); request_ids_add(request->id, db); jsonrpc_session_send(session, request); VLOG_DBG("Send monitor requests"); state = RPL_S_MONITOR_REQUESTED; } } } break; } case RPL_S_MONITOR_REQUESTED: { /* Reply to monitor requests. */ struct ovsdb_error *error; error = process_notification(msg->result, db); if (error) { ovsdb_error_assert(error); state = RPL_S_ERR; } else { /* Transition to replicating state after receiving * all replies of "monitor" requests. */ if (hmap_is_empty(&request_ids)) { VLOG_DBG("Listening to monitor updates"); state = RPL_S_REPLICATING; } } break; } case RPL_S_ERR: /* Ignore all messages */ break; case RPL_S_INIT: case RPL_S_REPLICATING: default: OVS_NOT_REACHED(); } }