/* 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); }
/* Terminate EVENT thread */ static int ovs_db_event_thread_terminate(ovs_db_t *pdb) { if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) { /* already terminated */ return 0; } ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE); if (pthread_join(pdb->event_thread.tid, NULL) != 0) return -1; /* Event thread always holds the thread mutex when * performs some task (handles event) and releases it when * while sleeping. Thus, if event thread exits, the mutex * remains locked */ pdb->event_thread.tid = (pthread_t){0}; pthread_mutex_unlock(&pdb->event_thread.mutex); return 0; }
/* 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; }