static bool try_command(json_t *cmd, int timeout) { w_stm_t client = NULL; w_jbuffer_t buffer; w_jbuffer_t output_pdu_buffer; int err; client = w_stm_connect(sock_name, timeout * 1000); if (client == NULL) { return false; } if (!cmd) { w_stm_close(client); return true; } w_json_buffer_init(&buffer); // Send command if (!w_ser_write_pdu(server_pdu, &buffer, client, cmd)) { err = errno; w_log(W_LOG_ERR, "error sending PDU to server\n"); w_json_buffer_free(&buffer); w_stm_close(client); errno = err; return false; } w_json_buffer_reset(&buffer); w_json_buffer_init(&output_pdu_buffer); do { if (!w_json_buffer_passthru( &buffer, output_pdu, &output_pdu_buffer, client)) { err = errno; w_json_buffer_free(&buffer); w_json_buffer_free(&output_pdu_buffer); w_stm_close(client); errno = err; return false; } } while (persistent); w_json_buffer_free(&buffer); w_json_buffer_free(&output_pdu_buffer); w_stm_close(client); return true; }
bool w_json_buffer_passthru(w_jbuffer_t *jr, enum w_pdu_type output_pdu, w_jbuffer_t *output_pdu_buf, w_stm_t stm) { json_t *j; json_error_t jerr; bool res; w_stm_set_nonblock(stm, false); if (!read_and_detect_pdu(jr, stm, &jerr)) { w_log(W_LOG_ERR, "failed to identify PDU: %s\n", jerr.text); return false; } if (jr->pdu_type == output_pdu) { // We can stream it through if (!stream_pdu(jr, stm, &jerr)) { w_log(W_LOG_ERR, "stream_pdu: %s\n", jerr.text); return false; } return true; } j = read_pdu_into_json(jr, stm, &jerr); if (!j) { w_log(W_LOG_ERR, "failed to parse response: %s\n", jerr.text); return false; } w_json_buffer_reset(output_pdu_buf); res = w_ser_write_pdu(output_pdu, output_pdu_buf, w_stm_stdout(), j); json_decref(j); return res; }
bool w_json_buffer_passthru(w_jbuffer_t *jr, enum w_pdu_type output_pdu, int fd) { json_t *j; json_error_t jerr; bool res; if (!read_and_detect_pdu(jr, fd, &jerr)) { w_log(W_LOG_ERR, "failed to identify PDU: %s\n", jerr.text); return false; } if (jr->pdu_type == output_pdu) { // We can stream it through if (!stream_pdu(jr, fd, &jerr)) { w_log(W_LOG_ERR, "stream_pdu: %s\n", jerr.text); return false; } return true; } j = read_pdu_into_json(jr, fd, &jerr); if (!j) { w_log(W_LOG_ERR, "failed to parse response: %s\n", jerr.text); return false; } w_json_buffer_reset(jr); res = w_ser_write_pdu(output_pdu, jr, STDOUT_FILENO, j); json_decref(j); return res; }
void preprocess_command(json_t *args, enum w_pdu_type output_pdu) { char *errmsg = NULL; struct watchman_command_handler_def *def; def = lookup(args, &errmsg, 0); if (!def && !errmsg) { // Nothing known about it, pass the command on anyway for forwards // compatibility return; } if (!errmsg && def->cli_validate) { def->cli_validate(args, &errmsg); } if (errmsg) { w_jbuffer_t jr; json_t *err = json_pack( "{s:s, s:s, s:b}", "error", errmsg, "version", PACKAGE_VERSION, "cli_validated", true ); w_json_buffer_init(&jr); w_ser_write_pdu(output_pdu, &jr, w_stm_stdout(), err); json_decref(err); w_json_buffer_free(&jr); free(errmsg); exit(1); } }
// 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; }
static bool try_command(json_t *cmd, int timeout) { int fd; int res; int tries; int bufsize; w_jbuffer_t buffer; fd = socket(PF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { perror("socket"); return false; } tries = 0; do { res = connect(fd, (struct sockaddr*)&un, sizeof(un)); if (res == 0) { break; } if (timeout && tries < timeout && should_start(errno)) { // Wait for socket to come up sleep(1); continue; } } while (++tries < timeout); if (res) { close(fd); return false; } if (!cmd) { close(fd); return true; } bufsize = WATCHMAN_IO_BUF_SIZE; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); w_json_buffer_init(&buffer); // Send command if (!w_ser_write_pdu(server_pdu, &buffer, fd, cmd)) { w_log(W_LOG_ERR, "error sending PDU to server\n"); w_json_buffer_free(&buffer); close(fd); return false; } w_json_buffer_reset(&buffer); do { if (!w_json_buffer_passthru(&buffer, output_pdu, fd)) { w_json_buffer_free(&buffer); close(fd); return false; } } while (persistent); w_json_buffer_free(&buffer); close(fd); return true; }
// 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; }
// 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 watchman_event_poll pfd[2]; json_t *request; json_error_t jerr; w_stm_set_nonblock(client->stm, true); w_set_thread_name("client:stm=%p", client->stm); w_stm_get_events(client->stm, &pfd[0].evt); pfd[1].evt = client->ping; 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 ignore_result(w_poll_events(pfd, 2, 2000)); if (stopping) { break; } if (pfd[0].ready) { request = w_json_buffer_next(&client->reader, client->stm, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool if (client->reader.wpos == client->reader.rpos) { // If they disconnected in between PDUs, no need to log // any error goto disconected; } 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].ready) { w_event_test_and_clear(client->ping); } /* 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) { bool ok; w_stm_set_nonblock(client->stm, false); /* Return the data in the same format that was used to ask for it */ ok = w_ser_write_pdu(client->pdu_type, &client->writer, client->stm, resp->json); json_decref(resp->json); free(resp); w_stm_set_nonblock(client->stm, true); if (!ok) { break; } } } } disconected: pthread_mutex_lock(&w_client_lock); w_ht_del(clients, w_ht_ptr_val(client)); 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 watchman_event_poll pfd[2]; struct watchman_client_response *queued_responses_to_send; json_t *request; json_error_t jerr; bool send_ok = true; w_stm_set_nonblock(client->stm, true); w_set_thread_name("client=%p:stm=%p", client, client->stm); client->client_is_owner = w_stm_peer_is_owner(client->stm); w_stm_get_events(client->stm, &pfd[0].evt); pfd[1].evt = client->ping; 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 ignore_result(w_poll_events(pfd, 2, 2000)); if (stopping) { break; } if (pfd[0].ready) { request = w_json_buffer_next(&client->reader, client->stm, &jerr); if (!request && errno == EAGAIN) { // That's fine } else if (!request) { // Not so cool if (client->reader.wpos == client->reader.rpos) { // If they disconnected in between PDUs, no need to log // any error goto disconected; } 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].ready) { w_event_test_and_clear(client->ping); } /* de-queue the pending responses under the lock */ pthread_mutex_lock(&w_client_lock); queued_responses_to_send = client->head; client->head = NULL; client->tail = NULL; pthread_mutex_unlock(&w_client_lock); /* now send our response(s) */ while (queued_responses_to_send) { struct watchman_client_response *response_to_send = queued_responses_to_send; if (send_ok) { w_stm_set_nonblock(client->stm, false); /* Return the data in the same format that was used to ask for it. * Don't bother sending any more messages if the client disconnects, * but still free their memory. */ send_ok = w_ser_write_pdu(client->pdu_type, &client->writer, client->stm, response_to_send->json); w_stm_set_nonblock(client->stm, true); } queued_responses_to_send = response_to_send->next; json_decref(response_to_send->json); free(response_to_send); } } disconected: w_set_thread_name("NOT_CONN:client=%p:stm=%p", client, client->stm); // Remove the client from the map before we tear it down, as this makes // it easier to flush out pending writes on windows without worrying // about w_log_to_clients contending for the write buffers pthread_mutex_lock(&w_client_lock); w_ht_del(clients, w_ht_ptr_val(client)); pthread_mutex_unlock(&w_client_lock); client_delete(client); return NULL; }