int mtev_json_object_to_fd(int fd, struct mtev_json_object *obj) { const char *mtev_json_str; int ret; unsigned int wpos, wsize; if(!obj) { MC_ERROR("mtev_json_object_to_fd: object is null%s\n", ""); return -1; } if(!(mtev_json_str = mtev_json_object_to_json_string(obj))) { return -1; } wsize = (unsigned int)(strlen(mtev_json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ wpos = 0; while(wpos < wsize) { if((ret = write(fd, mtev_json_str + wpos, wsize-wpos)) < 0) { MC_ERROR("mtev_json_object_to_fd: error writing fd %d: %s\n", fd, strerror(errno)); return -1; } /* because of the above check for ret < 0, we can safely cast and add */ wpos += (unsigned int)ret; } return 0; }
/** * The "noit_livestream" protocol requires a well formed request to come * in via websocket that resembles: * * { * "period_ms": <milliseconds>, * "check_uuid": "<uuid_string>", * "metrics" : ["<metric_name1>", "<metric_name2>"] * } * * There can be only one active livestream request for a single websocket at one time. * However, you can send down a new livestream request at any time to alter the * stream based on new requirements. * * If the "metrics" member exists in the incoming request, then the metrics that are sent will be * filtered to those that match the strings in the array by exact match. If you want to get * all metrics in the check, omit the "metrics" member from the request object. */ int noit_websocket_msg_handler(mtev_http_rest_closure_t *restc, int opcode, const unsigned char *msg, size_t msg_len) { #ifdef HAVE_WSLAY const char *error = NULL; noit_websocket_closure_t *handler_data = restc->call_closure; /* for now this message is JSON, that might change in the future */ struct mtev_json_tokener *tok = mtev_json_tokener_new(); struct mtev_json_object *request = mtev_json_tokener_parse_ex(tok, (const char *)msg, msg_len); enum mtev_json_tokener_error err = tok->err; mtev_json_tokener_free(tok); if (err != mtev_json_tokener_success) { error = "Unable to parse incoming json"; goto websocket_handler_error; } uint64_t period_ms = mtev_json_object_get_uint64(mtev_json_object_object_get(request, "period_ms")); const char *check_uuid = mtev_json_object_get_string(mtev_json_object_object_get(request, "check_uuid")); struct mtev_json_object *metrics = mtev_json_object_object_get(request, "metrics"); if (handler_data != NULL) { /* destroy old subscription */ noit_websocket_closure_free(handler_data); handler_data = NULL; } handler_data = restc->call_closure = noit_websocket_closure_alloc(); handler_data->use_filter = mtev_false; restc->call_closure_free = noit_websocket_closure_free; handler_data->period = period_ms; if (metrics != NULL) { handler_data->use_filter = mtev_true; handler_data->filter_count = mtev_json_object_array_length(metrics); handler_data->filters = calloc(sizeof(char *), handler_data->filter_count); jl_array_list* a = mtev_json_object_get_array(metrics); for (int i = 0; i < handler_data->filter_count; i++) { struct mtev_json_object *o = jl_array_list_get_idx(a, i); if (o != NULL) { handler_data->filters[i] = strdup(mtev_json_object_get_string(o)); } } } handler_data->restc = restc; strncpy(handler_data->uuid_str, check_uuid, UUID_STR_LEN); handler_data->uuid_str[36] = '\0'; if(uuid_parse(handler_data->uuid_str, handler_data->uuid)) { mtevL(noit_error, "bad uuid received in livestream websocket_handler '%s'\n", handler_data->uuid_str); error = "bad uuid received in livestream subscription request"; goto websocket_handler_error; } mtev_json_object_put(request); /* setup subscription to noit_livestream */ asprintf(&handler_data->feed, "websocket_livestream/%d", mtev_atomic_inc32(&ls_counter)); handler_data->log_stream = mtev_log_stream_new(handler_data->feed, "noit_websocket_livestream", handler_data->feed, handler_data, NULL); handler_data->check = noit_check_watch(handler_data->uuid, handler_data->period); if(!handler_data->check) { error = "Cannot locate check"; goto websocket_handler_error; } /* This check must be watched from the livestream */ noit_check_transient_add_feed(handler_data->check, handler_data->feed); /* Note the check */ noit_check_log_check(handler_data->check); /* kick it off, if it isn't running already */ if(!NOIT_CHECK_LIVE(handler_data->check)) noit_check_activate(handler_data->check); return 0; websocket_handler_error: { struct mtev_json_object *e = mtev_json_object_new_object(); mtev_json_object_object_add(e, "success", mtev_json_object_new_int(0)); struct mtev_json_object *errors = mtev_json_object_new_array(); struct mtev_json_object *error_o = mtev_json_object_new_object(); mtev_json_object_object_add(error_o, "error", mtev_json_object_new_string(error)); mtev_json_object_array_add(errors, error_o); mtev_json_object_object_add(e, "errors", errors); const char *json_error = mtev_json_object_to_json_string(e); mtev_http_websocket_queue_msg(restc->http_ctx, WSLAY_TEXT_FRAME | WSLAY_CONNECTION_CLOSE, (const unsigned char *)json_error, strlen(json_error)); mtev_json_object_put(e); } #endif return -1; }