+ struct ovsdb_jsonrpc_session *s;
+
+ LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &svr->sessions) {
+ ovsdb_jsonrpc_session_wait(s);
+ }
+}
+
+static struct jsonrpc_msg *
+execute_transaction(struct ovsdb_jsonrpc_session *s,
+ struct jsonrpc_msg *request)
+{
+ ovsdb_jsonrpc_trigger_create(s, request->id, request->params);
+ request->id = NULL;
+ request->params = NULL;
+ return NULL;
+}
+
+static void
+ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
+ struct jsonrpc_msg *request)
+{
+ struct jsonrpc_msg *reply;
+
+ if (!strcmp(request->method, "transact")) {
+ reply = execute_transaction(s, request);
+ } else if (!strcmp(request->method, "monitor")) {
+ reply = jsonrpc_create_reply(
+ ovsdb_jsonrpc_monitor_create(s, request->params), request->id);
+ } else if (!strcmp(request->method, "monitor_cancel")) {
+ reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params),
+ request->id);
+ } else if (!strcmp(request->method, "get_schema")) {
+ reply = jsonrpc_create_reply(
+ ovsdb_schema_to_json(s->server->db->schema), request->id);
+ } else if (!strcmp(request->method, "echo")) {
+ reply = jsonrpc_create_reply(json_clone(request->params), request->id);
+ } else {
+ reply = jsonrpc_create_error(json_string_create("unknown method"),
+ request->id);
+ }
+
+ if (reply) {
+ jsonrpc_msg_destroy(request);
+ jsonrpc_send(s->rpc, reply);
+ }
+}
+
+static void
+execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request)
+{
+ if (json_array(request->params)->n == 1) {
+ struct ovsdb_jsonrpc_trigger *t;
+ struct json *id;
+
+ id = request->params->u.array.elems[0];
+ t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0));
+ if (t) {
+ ovsdb_jsonrpc_trigger_complete(t);
+ }
+ }
+}
+
+static void
+ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s,
+ struct jsonrpc_msg *request)
+{
+ if (!strcmp(request->method, "cancel")) {
+ execute_cancel(s, request);
+ }
+ jsonrpc_msg_destroy(request);
+}
+\f
+/* JSON-RPC database server triggers.
+ *
+ * (Every transaction is treated as a trigger even if it doesn't actually have
+ * any "wait" operations.) */
+
+struct ovsdb_jsonrpc_trigger {
+ struct ovsdb_trigger trigger;
+ struct ovsdb_jsonrpc_session *session;
+ struct hmap_node hmap_node; /* In session's "triggers" hmap. */
+ struct json *id;
+};
+
+static void
+ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s,
+ struct json *id, struct json *params)
+{
+ struct ovsdb_jsonrpc_trigger *t;
+ size_t hash;
+
+ /* Check for duplicate ID. */
+ hash = json_hash(id, 0);
+ t = ovsdb_jsonrpc_trigger_find(s, id, hash);
+ if (t) {
+ jsonrpc_send(s->rpc, jsonrpc_create_error(
+ json_string_create("duplicate request ID"), id));
+ json_destroy(id);
+ json_destroy(params);
+ return;
+ }
+
+ /* Insert into trigger table. */
+ t = xmalloc(sizeof *t);
+ ovsdb_trigger_init(s->server->db,
+ &t->trigger, params, &s->completions,
+ time_msec());
+ t->session = s;
+ t->id = id;
+ hmap_insert(&s->triggers, &t->hmap_node, hash);
+
+ /* Complete early if possible. */
+ if (ovsdb_trigger_is_complete(&t->trigger)) {
+ ovsdb_jsonrpc_trigger_complete(t);