static int noit_console_watch_check(noit_console_closure_t ncct, int argc, char **argv, noit_console_state_t *state, void *closure) { int i, cnt; int adding = (int)(vpsized_int)closure; int period = 0; char xpath[1024]; xmlXPathObjectPtr pobj = NULL; xmlXPathContextPtr xpath_ctxt = NULL; noit_conf_xml_xpath(NULL, &xpath_ctxt); if(argc < 1 || argc > 2) { nc_printf(ncct, "requires one or two arguments\n"); return -1; } /* An alternate period */ if(argc == 2) period = atoi(argv[1]); if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), NULL, argc ? argv[0] : NULL)) { nc_printf(ncct, "ERROR: could not find check '%s'\n", argv[0]); return -1; } pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET || xmlXPathNodeSetIsEmpty(pobj->nodesetval)) { nc_printf(ncct, "no checks found\n"); goto out; } cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); for(i=0; i<cnt; i++) { uuid_t checkid; noit_check_t *check; xmlNodePtr node; char *uuid_conf; node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i); uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid"); if(!uuid_conf || uuid_parse(uuid_conf, checkid)) { nc_printf(ncct, "%s has invalid or missing UUID!\n", (char *)xmlGetNodePath(node) + strlen("/noit")); continue; } if(period == 0) { check = noit_poller_lookup(checkid); if(!check) continue; if(adding) noit_check_transient_add_feed(check, ncct->feed_path); else noit_check_transient_remove_feed(check, ncct->feed_path); } else { if(adding) { check = noit_check_watch(checkid, period); /* This check must be watched from the console */ noit_check_transient_add_feed(check, ncct->feed_path); /* Note the check */ noit_check_log_check(check); /* kick it off, if it isn't running already */ if(!NOIT_CHECK_LIVE(check)) noit_check_activate(check); } else { check = noit_check_get_watch(checkid, period); if(check) noit_check_transient_remove_feed(check, ncct->feed_path); } } } out: if(pobj) xmlXPathFreeObject(pobj); return 0; }
int noit_check_update(noit_check_t *new_check, const char *target, const char *name, const char *filterset, noit_hash_table *config, u_int32_t period, u_int32_t timeout, const char *oncheck, int flags) { int8_t family; int rv; int mask = NP_DISABLED | NP_UNCONFIG; union { struct in_addr addr4; struct in6_addr addr6; } a; family = AF_INET; rv = inet_pton(family, target, &a); if(rv != 1) { family = AF_INET6; rv = inet_pton(family, target, &a); if(rv != 1) { noitL(noit_stderr, "Cannot translate '%s' to IP\n", target); memset(&a, 0, sizeof(a)); flags |= (NP_UNCONFIG | NP_DISABLED); } } new_check->generation = __config_load_generation; new_check->target_family = family; memcpy(&new_check->target_addr, &a, sizeof(a)); if(new_check->target) free(new_check->target); new_check->target = strdup(target); if(new_check->name) free(new_check->name); new_check->name = name ? strdup(name): NULL; if(new_check->filterset) free(new_check->filterset); new_check->filterset = filterset ? strdup(filterset): NULL; if(config != NULL) { noit_hash_iter iter = NOIT_HASH_ITER_ZERO; const char *k; int klen; void *data; if(new_check->config) noit_hash_delete_all(new_check->config, free, free); else new_check->config = calloc(1, sizeof(*new_check->config)); while(noit_hash_next(config, &iter, &k, &klen, &data)) { noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data)); } } if(new_check->oncheck) free(new_check->oncheck); new_check->oncheck = oncheck ? strdup(oncheck) : NULL; new_check->period = period; new_check->timeout = timeout; /* Unset what could be set.. then set what should be set */ new_check->flags = (new_check->flags & ~mask) | flags; if(!(new_check->flags & NP_TRANSIENT)) { /* This remove could fail -- no big deal */ noit_skiplist_remove(&polls_by_name, new_check, NULL); /* This insert could fail.. which means we have a conflict on * target`name. That should result in the check being disabled. */ if(!noit_skiplist_insert(&polls_by_name, new_check)) { noitL(noit_stderr, "Check %s`%s disabled due to naming conflict\n", new_check->target, new_check->name); new_check->flags |= NP_DISABLED; } } noit_check_log_check(new_check); 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; }
int noit_livestream_handler(eventer_t e, int mask, void *closure, struct timeval *now) { eventer_t newe; pthread_t tid; int newmask = EVENTER_READ | EVENTER_EXCEPTION; acceptor_closure_t *ac = closure; noit_livestream_closure_t *jcl = ac->service_ctx; if(mask & EVENTER_EXCEPTION || (jcl && jcl->wants_shutdown)) { socket_error: /* Exceptions cause us to simply snip the connection */ eventer_remove_fd(e->fd); e->opset->close(e->fd, &newmask, e); if(jcl) noit_livestream_closure_free(jcl); if(ac) acceptor_closure_free(ac); return 0; } if(!ac->service_ctx || !jcl->feed) { int len; jcl = ac->service_ctx = noit_livestream_closure_alloc(); /* Setup logger to this channel */ if(!jcl->period) { u_int32_t nperiod; len = e->opset->read(e->fd, &nperiod, sizeof(nperiod), &mask, e); if(len == -1 && errno == EAGAIN) return mask | EVENTER_EXCEPTION; if(len != sizeof(nperiod)) goto socket_error; jcl->period = ntohl(nperiod); if(!jcl->period) { noitL(noit_error, "period of 0 specified in livestream. not allowed.\n"); goto socket_error; } } while(jcl->uuid_read < 36) { len = e->opset->read(e->fd, jcl->uuid_str + jcl->uuid_read, 36 - jcl->uuid_read, &mask, e); if(len == -1 && errno == EAGAIN) return mask | EVENTER_EXCEPTION; if(len == 0) goto socket_error; jcl->uuid_read += len; } jcl->uuid_str[36] = '\0'; if(uuid_parse(jcl->uuid_str, jcl->uuid)) { noitL(noit_error, "bad uuid received in livestream handler '%s'\n", jcl->uuid_str); goto socket_error; } jcl->feed = malloc(32); snprintf(jcl->feed, 32, "livestream/%d", noit_atomic_inc32(&ls_counter)); noit_log_stream_new(jcl->feed, "noit_livestream", jcl->feed, jcl, NULL); jcl->check = noit_check_watch(jcl->uuid, jcl->period); if(!jcl->check) { e->opset->close(e->fd, &newmask, e); return 0; } /* This check must be watched from the livestream */ noit_check_transient_add_feed(jcl->check, jcl->feed); /* Note the check */ noit_check_log_check(jcl->check); /* kick it off, if it isn't running already */ if(!NOIT_CHECK_LIVE(jcl->check)) noit_check_activate(jcl->check); } eventer_remove_fd(e->fd); newe = eventer_alloc(); memcpy(newe, e, sizeof(*e)); if(pthread_create(&tid, NULL, noit_livestream_thread_main, newe) == 0) { return 0; } noit_check_transient_remove_feed(jcl->check, jcl->feed); noit_livestream_closure_free(jcl); /* Undo our dup */ eventer_free(newe); /* Creating the thread failed, close it down and deschedule. */ e->opset->close(e->fd, &newmask, e); return 0; }