static json_t *read_bser_pdu(w_jbuffer_t *jr, int fd, json_error_t *jerr) { int needed; json_int_t val; uint32_t ideal; int need; int r; json_t *obj; jr->rpos += 2; // We don't handle EAGAIN cleanly in here w_clear_nonblock(fd); if (!w_bser_decode_pdu_len(jr, fd, &val, jerr)) { return NULL; } // val tells us exactly how much storage we need for this PDU need = val - (jr->allocd - jr->wpos); if (need > 0) { ideal = jr->allocd; while (ideal < (uint32_t)need) { ideal *= 2; } if (ideal > jr->allocd) { char *buf = realloc(jr->buf, ideal); if (!buf) { snprintf(jerr->text, sizeof(jerr->text), "out of memory while allocating %" PRIu32 " bytes", ideal); return NULL; } jr->buf = buf; jr->allocd = ideal; } } // We have enough room for the whole thing, let's read it in while ((jr->wpos - jr->rpos) < val) { r = read(fd, jr->buf + jr->wpos, jr->allocd - jr->wpos); if (r <= 0) { snprintf(jerr->text, sizeof(jerr->text), "error reading PDU: %s", strerror(errno)); return NULL; } jr->wpos += r; } obj = bunser(jr->buf + jr->rpos, jr->buf + jr->wpos, &needed, jerr); // Ensure that we move the read position to the wpos; we consumed it all jr->rpos = jr->wpos; w_set_nonblock(fd); return obj; }
static void unix_set_nonb(w_stm_t stm, bool nonb) { struct unix_handle *h = stm->handle; if (nonb) { w_set_nonblock(h->fd); } else { w_clear_nonblock(h->fd); } }
// The client thread reads and decodes json packets, // then dispatches the commands that it finds static void *client_thread(void *ptr) { struct watchman_client *client = ptr; struct pollfd pfd[2]; json_t *request; json_error_t jerr; char buf[16]; w_set_nonblock(client->fd); while (!stopping) { // Wait for input from either the client socket or // via the ping pipe, which signals that some other // thread wants to unilaterally send data to the client pfd[0].fd = client->fd; pfd[0].events = POLLIN|POLLHUP|POLLERR; pfd[0].revents = 0; pfd[1].fd = client->ping[0]; pfd[1].events = POLLIN|POLLHUP|POLLERR; pfd[1].revents = 0; ignore_result(poll(pfd, 2, 200)); if (stopping) { break; } if (pfd[0].revents & (POLLHUP|POLLERR)) { w_log(W_LOG_DBG, "got HUP|ERR on client %p fd=%d, disconnecting\n", client, client->fd); break; } if (pfd[0].revents) { // Solaris: we may not detect POLLHUP until we try to read, so // let's peek ahead and characterize it correctly. This is only // needed if we have no data buffered if (client->reader.wpos == client->reader.rpos) { char peek; if (recv(client->fd, &peek, sizeof(peek), MSG_PEEK) == 0) { w_log(W_LOG_DBG, "got HUP|ERR on client fd=%d, disconnecting\n", client->fd); goto disconected; } } request = w_json_buffer_next(&client->reader, client->fd, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool send_error_response(client, "invalid json at position %d: %s", jerr.position, jerr.text); w_log(W_LOG_ERR, "invalid data from client: %s\n", jerr.text); goto disconected; } else if (request) { client->pdu_type = client->reader.pdu_type; dispatch_command(client, request, CMD_DAEMON); json_decref(request); } } if (pfd[1].revents) { ignore_result(read(client->ping[0], buf, sizeof(buf))); } /* now send our response(s) */ while (client->head) { struct watchman_client_response *resp; /* de-queue the first response */ pthread_mutex_lock(&w_client_lock); resp = client->head; if (resp) { client->head = resp->next; if (client->tail == resp) { client->tail = NULL; } } pthread_mutex_unlock(&w_client_lock); if (resp) { w_clear_nonblock(client->fd); /* Return the data in the same format that was used to ask for it */ w_ser_write_pdu(client->pdu_type, &client->writer, client->fd, resp->json); json_decref(resp->json); free(resp); w_set_nonblock(client->fd); } } } disconected: pthread_mutex_lock(&w_client_lock); w_ht_del(clients, client->fd); pthread_mutex_unlock(&w_client_lock); return NULL; }
// The client thread reads and decodes json packets, // then dispatches the commands that it finds static void *client_thread(void *ptr) { struct watchman_client *client = ptr; struct pollfd pfd[2]; json_t *request; json_error_t jerr; char buf[16]; w_set_nonblock(client->fd); while (true) { // Wait for input from either the client socket or // via the ping pipe, which signals that some other // thread wants to unilaterally send data to the client pfd[0].fd = client->fd; pfd[0].events = POLLIN|POLLHUP|POLLERR; pfd[0].revents = 0; pfd[1].fd = client->ping[0]; pfd[1].events = POLLIN|POLLHUP|POLLERR; pfd[1].revents = 0; ignore_result(poll(pfd, 2, 200)); if (pfd[0].revents & (POLLHUP|POLLERR)) { disconected: pthread_mutex_lock(&w_client_lock); w_ht_del(clients, client->fd); pthread_mutex_unlock(&w_client_lock); break; } if (pfd[0].revents) { request = w_json_buffer_next(&client->reader, client->fd, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool send_error_response(client, "invalid json at position %d: %s", jerr.position, jerr.text); w_log(W_LOG_ERR, "invalid data from client: %s\n", jerr.text); goto disconected; } else if (request) { client->pdu_type = client->reader.pdu_type; dispatch_command(client, request); json_decref(request); } } if (pfd[1].revents) { ignore_result(read(client->ping[0], buf, sizeof(buf))); } /* now send our response(s) */ while (client->head) { struct watchman_client_response *resp; /* de-queue the first response */ pthread_mutex_lock(&w_client_lock); resp = client->head; if (resp) { client->head = resp->next; if (client->tail == resp) { client->tail = NULL; } } pthread_mutex_unlock(&w_client_lock); if (resp) { w_clear_nonblock(client->fd); /* Return the data in the same format that was used to ask for it */ w_ser_write_pdu(client->pdu_type, &client->writer, client->fd, resp->json); json_decref(resp->json); free(resp); w_set_nonblock(client->fd); } } } return NULL; }