/* Reconnect to OVS DB and call the OVS DB post connection init callback * if connection has been established. */ static void ovs_db_reconnect(ovs_db_t *pdb) { const char *node_info = pdb->node; struct addrinfo *result; if (pdb->unix_path[0] != '\0') { /* use UNIX socket instead of INET address */ node_info = pdb->unix_path; result = calloc(1, sizeof(struct addrinfo)); struct sockaddr_un *sa_unix = calloc(1, sizeof(struct sockaddr_un)); if (result == NULL || sa_unix == NULL) { sfree(result); sfree(sa_unix); return; } result->ai_family = AF_UNIX; result->ai_socktype = SOCK_STREAM; result->ai_addrlen = sizeof(*sa_unix); result->ai_addr = (struct sockaddr *)sa_unix; sa_unix->sun_family = result->ai_family; sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path)); } else { /* inet socket address */ struct addrinfo hints; /* setup criteria for selecting the socket address */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; /* get socket addresses */ int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result); if (ret != 0) { OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret)); return; } } /* try to connect to the server */ for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock < 0) { OVS_DEBUG("socket(): %s", STRERRNO); continue; } if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) { close(sock); OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family); } else { /* send notification to event thread */ pdb->sock = sock; ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED); break; } } if (pdb->sock < 0) OVS_ERROR("connect to \"%s\" failed", node_info); freeaddrinfo(result); }
/* Handle JSON data (one request) and call * appropriate event OVS DB handler. Currently, * update callback 'ovs_db_table_update_cb' and * result callback 'ovs_db_result_cb' is supported. */ static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data, size_t len) { const char *method = NULL; char yajl_errbuf[OVS_YAJL_ERROR_BUFFER_SIZE]; const char *method_path[] = {"method", NULL}; const char *result_path[] = {"result", NULL}; char *sjson = NULL; yajl_val jnode, jval; /* duplicate the data to make null-terminated string * required for yajl_tree_parse() */ if ((sjson = calloc(1, len + 1)) == NULL) return -1; sstrncpy(sjson, data, len + 1); OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson); /* parse json data */ jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf)); if (jnode == NULL) { OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf); sfree(sjson); return -1; } /* get method name */ if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) { if ((method = YAJL_GET_STRING(jval)) == NULL) { yajl_tree_free(jnode); sfree(sjson); return -1; } if (strcmp("echo", method) == 0) { /* echo request from the server */ if (ovs_db_table_echo_cb(pdb, jnode) < 0) OVS_ERROR("handle echo request failed"); } else if (strcmp("update", method) == 0) { /* update notification */ if (ovs_db_table_update_cb(pdb, jnode) < 0) OVS_ERROR("handle update notification failed"); } } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) { /* result notification */ if (ovs_db_result_cb(pdb, jnode) < 0) OVS_ERROR("handle result reply failed"); } else OVS_ERROR("connot find method or result failed"); /* release memory */ yajl_tree_free(jnode); sfree(sjson); return 0; }
/* Add YAJL value into YAJL generator handle (JSON object) * * jgen - YAJL generator handle allocated by yajl_gen_alloc() * jval - YAJL value usually returned by yajl_tree_get() */ static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) { size_t array_len = 0; yajl_val *jvalues = NULL; yajl_val jobj_value = NULL; const char *obj_key = NULL; size_t obj_len = 0; yajl_gen_status yajl_gen_ret = yajl_gen_status_ok; if (jval == NULL) return yajl_gen_generation_complete; if (YAJL_IS_STRING(jval)) OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, YAJL_GET_STRING(jval)); else if (YAJL_IS_DOUBLE(jval)) OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_DOUBLE(jval)); else if (YAJL_IS_INTEGER(jval)) OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_INTEGER(jval)); else if (YAJL_IS_TRUE(jval)) OVS_YAJL_CALL(yajl_gen_bool, jgen, 1); else if (YAJL_IS_FALSE(jval)) OVS_YAJL_CALL(yajl_gen_bool, jgen, 0); else if (YAJL_IS_NULL(jval)) OVS_YAJL_CALL(yajl_gen_null, jgen); else if (YAJL_IS_ARRAY(jval)) { /* create new array and add all elements into the array */ array_len = YAJL_GET_ARRAY(jval)->len; jvalues = YAJL_GET_ARRAY(jval)->values; OVS_YAJL_CALL(yajl_gen_array_open, jgen); for (size_t i = 0; i < array_len; i++) OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jvalues[i]); OVS_YAJL_CALL(yajl_gen_array_close, jgen); } else if (YAJL_IS_OBJECT(jval)) { /* create new object and add all elements into the object */ OVS_YAJL_CALL(yajl_gen_map_open, jgen); obj_len = YAJL_GET_OBJECT(jval)->len; for (size_t i = 0; i < obj_len; i++) { obj_key = YAJL_GET_OBJECT(jval)->keys[i]; jobj_value = YAJL_GET_OBJECT(jval)->values[i]; OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, obj_key); OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jobj_value); } OVS_YAJL_CALL(yajl_gen_map_close, jgen); } else { OVS_ERROR("%s() unsupported value type %d (skip)", __FUNCTION__, (int)(jval)->type); goto yajl_gen_failure; } return yajl_gen_status_ok; yajl_gen_failure: OVS_ERROR("%s() error to generate value", __FUNCTION__); return yajl_gen_ret; }
/* OVS DB echo request handler. When OVS DB sends * "echo" request to the client, client should generate * "echo" replay with the same content received in the * request */ static int ovs_db_table_echo_cb(const ovs_db_t *pdb, yajl_val jnode) { yajl_val jparams; yajl_val jid; yajl_gen jgen; size_t resp_len = 0; const char *resp = NULL; const char *params_path[] = {"params", NULL}; const char *id_path[] = {"id", NULL}; yajl_gen_status yajl_gen_ret; if ((jgen = yajl_gen_alloc(NULL)) == NULL) return -1; /* check & get request attributes */ if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || ((jid = yajl_tree_get(jnode, id_path, yajl_t_any)) == NULL)) { OVS_ERROR("parse echo request failed"); goto yajl_gen_failure; } /* generate JSON echo response */ OVS_YAJL_CALL(yajl_gen_map_open, jgen); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "result"); OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "error"); OVS_YAJL_CALL(yajl_gen_null, jgen); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jid); OVS_YAJL_CALL(yajl_gen_map_close, jgen); OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&resp, &resp_len); /* send the response */ OVS_DEBUG("response: %s", resp); if (ovs_db_data_send(pdb, resp, resp_len) < 0) { OVS_ERROR("send echo reply failed"); goto yajl_gen_failure; } /* clean up and return success */ yajl_gen_clear(jgen); return 0; yajl_gen_failure: /* release memory */ yajl_gen_clear(jgen); return -1; }
/* OVS DB table update event handler. * This callback is called by POLL thread if OVS DB * table update callback is received from the DB * server. Once registered callback found, it's called * by this handler. */ static int ovs_db_table_update_cb(ovs_db_t *pdb, yajl_val jnode) { ovs_callback_t *cb = NULL; yajl_val jvalue; yajl_val jparams; yajl_val jtable_updates; const char *params_path[] = {"params", NULL}; const char *id_path[] = {"id", NULL}; /* check & get request attributes */ if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || (yajl_tree_get(jnode, id_path, yajl_t_null) == NULL)) { OVS_ERROR("invalid OVS DB request received"); return -1; } /* check array length: [<json-value>, <table-updates>] */ if ((YAJL_GET_ARRAY(jparams) == NULL) || (YAJL_GET_ARRAY(jparams)->len != 2)) { OVS_ERROR("invalid OVS DB request received"); return -1; } jvalue = YAJL_GET_ARRAY(jparams)->values[0]; jtable_updates = YAJL_GET_ARRAY(jparams)->values[1]; if ((!YAJL_IS_OBJECT(jtable_updates)) || (!YAJL_IS_STRING(jvalue))) { OVS_ERROR("invalid OVS DB request id or table update received"); return -1; } /* find registered callback based on <json-value> */ pthread_mutex_lock(&pdb->mutex); cb = ovs_db_table_callback_get(pdb, jvalue); if (cb == NULL || cb->table.call == NULL) { OVS_ERROR("No OVS DB table update callback found"); pthread_mutex_unlock(&pdb->mutex); return -1; } /* call registered callback */ cb->table.call(jtable_updates); pthread_mutex_unlock(&pdb->mutex); return 0; }
int ovs_db_destroy(ovs_db_t *pdb) { int ovs_db_ret = 0; int ret = 0; /* sanity check */ if (pdb == NULL) return -1; /* stop event thread */ if (ovs_db_event_thread_terminate(pdb) < 0) { OVS_ERROR("stop event thread failed"); ovs_db_ret = -1; } /* try to lock the structure before releasing */ if ((ret = pthread_mutex_lock(&pdb->mutex))) { OVS_ERROR("pthread_mutex_lock() DB mutex lock failed (%d)", ret); return ret; } /* stop poll thread and destroy thread's private data */ if (ovs_db_poll_thread_destroy(pdb) < 0) { OVS_ERROR("destroy poll thread failed"); ovs_db_ret = -1; } /* destroy event thread private data */ ovs_db_event_thread_data_destroy(pdb); pthread_mutex_unlock(&pdb->mutex); /* unsubscribe callbacks */ ovs_db_callback_remove_all(pdb); /* close connection */ if (pdb->sock >= 0) close(pdb->sock); /* release DB handler */ pthread_mutex_destroy(&pdb->mutex); sfree(pdb); return ovs_db_ret; }
/* POLL worker thread. * It listens on OVS DB connection for incoming * requests/reply/events etc. Also, it reconnects to OVS DB * if connection has been lost. */ static void *ovs_poll_worker(void *arg) { ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */ ovs_json_reader_t *jreader = NULL; struct pollfd poll_fd = { .fd = pdb->sock, .events = POLLIN | POLLPRI, .revents = 0, }; /* create JSON reader instance */ if ((jreader = ovs_json_reader_alloc()) == NULL) { OVS_ERROR("initialize json reader failed"); return NULL; } /* poll data */ while (ovs_db_poll_is_running(pdb)) { poll_fd.fd = pdb->sock; int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000); if (poll_ret < 0) { OVS_ERROR("poll(): %s", STRERRNO); break; } else if (poll_ret == 0) { OVS_DEBUG("poll(): timeout"); if (pdb->sock < 0) /* invalid fd, so try to reconnect */ ovs_db_reconnect(pdb); continue; } if (poll_fd.revents & POLLNVAL) { /* invalid file descriptor, clean-up */ ovs_db_callback_remove_all(pdb); ovs_json_reader_reset(jreader); /* setting poll FD to -1 tells poll() call to ignore this FD. * In that case poll() call will return timeout all the time */ pdb->sock = (-1); } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) { /* connection is broken */ close(poll_fd.fd); ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); OVS_ERROR("poll() peer closed its end of the channel"); } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) { /* read incoming data */ char buff[OVS_DB_POLL_READ_BLOCK_SIZE]; ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0); if (nbytes < 0) { OVS_ERROR("recv(): %s", STRERRNO); /* read error? Try to reconnect */ close(poll_fd.fd); continue; } else if (nbytes == 0) { close(poll_fd.fd); ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); OVS_ERROR("recv() peer has performed an orderly shutdown"); continue; } /* read incoming data */ size_t json_len = 0; const char *json = NULL; OVS_DEBUG("recv(): received %zd bytes of data", nbytes); ovs_json_reader_push_data(jreader, buff, nbytes); while (!ovs_json_reader_pop(jreader, &json, &json_len)) /* process JSON data */ ovs_db_json_data_process(pdb, json, json_len); } } OVS_DEBUG("poll thread has been completed"); ovs_json_reader_free(jreader); return NULL; } /* EVENT worker thread. * Perform task based on incoming events. This * task can be done asynchronously which allows to * handle OVS DB callback like 'init_cb'. */ static void *ovs_event_worker(void *arg) { ovs_db_t *pdb = (ovs_db_t *)arg; while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) { /* wait for an event */ struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += (OVS_DB_EVENT_TIMEOUT); int ret = pthread_cond_timedwait(&pdb->event_thread.cond, &pdb->event_thread.mutex, &ts); if (!ret || ret == ETIMEDOUT) { /* handle the event */ OVS_DEBUG("handle event %d", pdb->event_thread.value); switch (pdb->event_thread.value) { case OVS_DB_EVENT_CONN_ESTABLISHED: if (pdb->cb.post_conn_init) pdb->cb.post_conn_init(pdb); /* reset event */ pdb->event_thread.value = OVS_DB_EVENT_NONE; break; case OVS_DB_EVENT_CONN_TERMINATED: if (pdb->cb.post_conn_terminate) pdb->cb.post_conn_terminate(); /* reset event */ pdb->event_thread.value = OVS_DB_EVENT_NONE; break; case OVS_DB_EVENT_NONE: /* wait timeout */ OVS_DEBUG("no event received (timeout)"); break; default: OVS_DEBUG("unknown event received"); break; } } else { /* unexpected error */ OVS_ERROR("pthread_cond_timedwait() failed"); break; } } OVS_DEBUG("event thread has been completed"); return NULL; }
int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, const char **tb_column, ovs_db_table_cb_t update_cb, ovs_db_result_cb_t result_cb, unsigned int flags) { yajl_gen jgen; yajl_gen_status yajl_gen_ret; ovs_callback_t *new_cb = NULL; char uid_str[OVS_UID_STR_SIZE]; char *params; size_t params_len; int ovs_db_ret = 0; /* sanity check */ if (pdb == NULL || tb_name == NULL || update_cb == NULL) return -1; /* allocate new update callback */ if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) return -1; /* init YAJL generator */ if ((jgen = yajl_gen_alloc(NULL)) == NULL) { sfree(new_cb); return -1; } /* add new callback to front */ new_cb->table.call = update_cb; new_cb->uid = ovs_uid_generate(); ovs_db_callback_add(pdb, new_cb); /* make update notification request * [<db-name>, <json-value>, <monitor-requests>] */ OVS_YAJL_CALL(yajl_gen_array_open, jgen); { OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME); /* uid string <json-value> */ snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str); /* <monitor-requests> */ OVS_YAJL_CALL(yajl_gen_map_open, jgen); { OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, tb_name); OVS_YAJL_CALL(yajl_gen_array_open, jgen); { /* <monitor-request> */ OVS_YAJL_CALL(yajl_gen_map_open, jgen); { if (tb_column) { /* columns within the table to be monitored */ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "columns"); OVS_YAJL_CALL(yajl_gen_array_open, jgen); for (; *tb_column; tb_column++) OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, *tb_column); OVS_YAJL_CALL(yajl_gen_array_close, jgen); } /* specify select option */ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "select"); { OVS_YAJL_CALL(yajl_gen_map_open, jgen); { OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "initial"); OVS_YAJL_CALL(yajl_gen_bool, jgen, flags & OVS_DB_TABLE_CB_FLAG_INITIAL); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "insert"); OVS_YAJL_CALL(yajl_gen_bool, jgen, flags & OVS_DB_TABLE_CB_FLAG_INSERT); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "delete"); OVS_YAJL_CALL(yajl_gen_bool, jgen, flags & OVS_DB_TABLE_CB_FLAG_DELETE); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "modify"); OVS_YAJL_CALL(yajl_gen_bool, jgen, flags & OVS_DB_TABLE_CB_FLAG_MODIFY); } OVS_YAJL_CALL(yajl_gen_map_close, jgen); } } OVS_YAJL_CALL(yajl_gen_map_close, jgen); } OVS_YAJL_CALL(yajl_gen_array_close, jgen); } OVS_YAJL_CALL(yajl_gen_map_close, jgen); } OVS_YAJL_CALL(yajl_gen_array_close, jgen); /* make a request to subscribe to given table */ OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)¶ms, ¶ms_len); if (ovs_db_send_request(pdb, "monitor", params, result_cb) < 0) { OVS_ERROR("Failed to subscribe to \"%s\" table", tb_name); ovs_db_ret = (-1); } yajl_gen_failure: /* release memory */ yajl_gen_clear(jgen); return ovs_db_ret; }
int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params, ovs_db_result_cb_t cb) { int ret = 0; yajl_gen_status yajl_gen_ret; yajl_val jparams; yajl_gen jgen; ovs_callback_t *new_cb = NULL; uint64_t uid; char uid_buff[OVS_UID_STR_SIZE]; const char *req = NULL; size_t req_len = 0; struct timespec ts; /* sanity check */ if (!pdb || !method || !params) return -1; if ((jgen = yajl_gen_alloc(NULL)) == NULL) return -1; /* try to parse params */ if ((jparams = yajl_tree_parse(params, NULL, 0)) == NULL) { OVS_ERROR("params is not a JSON string"); yajl_gen_clear(jgen); return -1; } /* generate method field */ OVS_YAJL_CALL(yajl_gen_map_open, jgen); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "method"); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, method); /* generate params field */ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "params"); OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); yajl_tree_free(jparams); /* generate id field */ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); uid = ovs_uid_generate(); snprintf(uid_buff, sizeof(uid_buff), "%" PRIX64, uid); OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_buff); OVS_YAJL_CALL(yajl_gen_map_close, jgen); if (cb) { /* register result callback */ if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) goto yajl_gen_failure; /* add new callback to front */ sem_init(&new_cb->result.sync, 0, 0); new_cb->result.call = cb; new_cb->uid = uid; ovs_db_callback_add(pdb, new_cb); } /* send the request */ OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&req, &req_len); OVS_DEBUG("%s", req); if (!ovs_db_data_send(pdb, req, req_len)) { if (cb) { /* wait for result */ clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += OVS_DB_SEND_REQ_TIMEOUT; if (sem_timedwait(&new_cb->result.sync, &ts) < 0) { OVS_ERROR("%s() no replay received within %d sec", __FUNCTION__, OVS_DB_SEND_REQ_TIMEOUT); ret = (-1); } } } else { OVS_ERROR("ovs_db_data_send() failed"); ret = (-1); } yajl_gen_failure: if (new_cb) { /* destroy callback */ sem_destroy(&new_cb->result.sync); ovs_db_callback_remove(pdb, new_cb); } /* release memory */ yajl_gen_clear(jgen); return (yajl_gen_ret != yajl_gen_status_ok) ? (-1) : ret; }
ovs_db_t *ovs_db_init(const char *node, const char *service, const char *unix_path, ovs_db_callback_t *cb) { int ret; /* sanity check */ if (node == NULL || service == NULL || unix_path == NULL) return NULL; /* allocate db data & fill it */ ovs_db_t *pdb = calloc(1, sizeof(*pdb)); if (pdb == NULL) return NULL; pdb->sock = -1; /* store the OVS DB address */ sstrncpy(pdb->node, node, sizeof(pdb->node)); sstrncpy(pdb->service, service, sizeof(pdb->service)); sstrncpy(pdb->unix_path, unix_path, sizeof(pdb->unix_path)); /* setup OVS DB callbacks */ if (cb) pdb->cb = *cb; /* init OVS DB mutex attributes */ pthread_mutexattr_t mutex_attr; if (pthread_mutexattr_init(&mutex_attr)) { OVS_ERROR("OVS DB mutex attribute init failed"); sfree(pdb); return NULL; } /* set OVS DB mutex as recursive */ if (pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE)) { OVS_ERROR("Failed to set OVS DB mutex as recursive"); pthread_mutexattr_destroy(&mutex_attr); sfree(pdb); return NULL; } /* init OVS DB mutex */ if (pthread_mutex_init(&pdb->mutex, &mutex_attr)) { OVS_ERROR("OVS DB mutex init failed"); pthread_mutexattr_destroy(&mutex_attr); sfree(pdb); return NULL; } /* destroy mutex attributes */ pthread_mutexattr_destroy(&mutex_attr); /* init event thread */ if (ovs_db_event_thread_init(pdb) < 0) { ret = ovs_db_destroy(pdb); if (ret > 0) goto failure; else return NULL; } /* init polling thread */ if (ovs_db_poll_thread_init(pdb) < 0) { ret = ovs_db_destroy(pdb); if (ret > 0) { ovs_db_event_thread_data_destroy(pdb); goto failure; } else { return NULL; } } return pdb; failure: pthread_mutex_destroy(&pdb->mutex); sfree(pdb); return NULL; }