int noit_check_activate(noit_check_t *check) { noit_module_t *mod; if(NOIT_CHECK_LIVE(check)) return 0; mod = noit_module_lookup(check->module); if(mod && mod->initiate_check) { if((check->flags & NP_DISABLED) == 0) { mod->initiate_check(mod, check, 0, NULL); return 1; } else noitL(noit_debug, "Skipping %s`%s, disabled.\n", check->target, check->name); } else { if(!mod) { noitL(noit_stderr, "Cannot find module '%s'\n", check->module); check->flags |= NP_DISABLED; } } return 0; }
static int replace_attr(noit_console_closure_t ncct, noit_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo, const char *value) { int i, cnt, rv = -1, active = 0; xmlXPathObjectPtr pobj = NULL; xmlXPathContextPtr xpath_ctxt = NULL; xmlNodePtr node; char xpath[1024], *path; path = info->path; if(!strcmp(path, "/")) path = ""; noit_conf_xml_xpath(NULL, &xpath_ctxt); if(attrinfo->checks_fixate) { /* Only if checks will fixate this attribute shall we check for * child <check> nodes. * NOTE: this return nothing and "seems" okay if we are _in_ * a <check> node. That case is handled below. */ snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); for(i=0; i<cnt; i++) { uuid_t checkid; node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i); if(noit_conf_get_uuid(node, "@uuid", checkid)) { noit_check_t *check; check = noit_poller_lookup(checkid); if(check && NOIT_CHECK_LIVE(check)) active++; } } if(pobj) xmlXPathFreeObject(pobj); } snprintf(xpath, sizeof(xpath), "/noit/%s", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); if(cnt != 1) { nc_printf(ncct, "Internal error: context node disappeared\n"); goto out; } node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0); if(attrinfo->checks_fixate && !strcmp((const char *)node->name, "check")) { uuid_t checkid; /* Detect if we are actually a <check> node and attempting to * change something we shouldn't. * This is the counterpart noted above. */ if(noit_conf_get_uuid(node, "@uuid", checkid)) { noit_check_t *check; check = noit_poller_lookup(checkid); if(check && NOIT_CHECK_LIVE(check)) active++; } } if(active) { nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n", attrinfo->name, active); goto out; } xmlUnsetProp(node, (xmlChar *)attrinfo->name); if(value) xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value); noit_conf_mark_changed(); rv = 0; out: if(pobj) xmlXPathFreeObject(pobj); return rv; }
static int replace_config(noit_console_closure_t ncct, noit_conf_t_userdata_t *info, const char *name, const char *value) { int i, cnt, rv = -1, active = 0; xmlXPathObjectPtr pobj = NULL; xmlXPathContextPtr xpath_ctxt = NULL; xmlNodePtr node, confignode; char xpath[1024], *path; path = info->path; if(!strcmp(path, "/")) path = ""; noit_conf_xml_xpath(NULL, &xpath_ctxt); /* Only if checks will fixate this attribute shall we check for * child <check> nodes. * NOTE: this return nothing and "seems" okay if we are _in_ * a <check> node. That case is handled below. */ snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); for(i=0; i<cnt; i++) { uuid_t checkid; node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i); if(noit_conf_get_uuid(node, "@uuid", checkid)) { noit_check_t *check; check = noit_poller_lookup(checkid); if(check && NOIT_CHECK_LIVE(check)) active++; } } if(pobj) xmlXPathFreeObject(pobj); #ifdef UNSAFE_RECONFIG snprintf(xpath, sizeof(xpath), "/noit/%s", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; cnt = xmlXPathNodeSetGetLength(pobj->nodesetval); if(cnt != 1) { nc_printf(ncct, "Internal error: context node disappeared\n"); goto out; } node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0); if(strcmp((const char *)node->name, "check")) { uuid_t checkid; /* Detect if we are actually a <check> node and attempting to * change something we shouldn't. * This is the counterpart noted above. */ if(noit_conf_get_uuid(node, "@uuid", checkid)) { noit_check_t *check; check = noit_poller_lookup(checkid); if(NOIT_CHECK_LIVE(check)) active++; } } if(active) { nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n", name, active); goto out; } if(pobj) xmlXPathFreeObject(pobj); #endif /* Here we want to remove /noit/path/config/name */ snprintf(xpath, sizeof(xpath), "/noit/%s/config/%s", path, name); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 0) { xmlNodePtr toremove; toremove = xmlXPathNodeSetItem(pobj->nodesetval, 0); xmlUnlinkNode(toremove); } /* TODO: if there are no more children of config, remove config? */ if(value) { if(pobj) xmlXPathFreeObject(pobj); /* He we create config if needed and place a child node under it */ snprintf(xpath, sizeof(xpath), "/noit/%s/config", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 0) { if(pobj) xmlXPathFreeObject(pobj); snprintf(xpath, sizeof(xpath), "/noit/%s", path); pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt); if(!pobj || pobj->type != XPATH_NODESET) goto out; if(xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) { nc_printf(ncct, "Node disappeared from under you!\n"); goto out; } confignode = xmlNewChild(xmlXPathNodeSetItem(pobj->nodesetval, 0), NULL, (xmlChar *)"config", NULL); if(confignode == NULL) { nc_printf(ncct, "Error creating config child node.\n"); goto out; } } else confignode = xmlXPathNodeSetItem(pobj->nodesetval, 0); assert(confignode); /* Now we create a child */ xmlNewChild(confignode, NULL, (xmlChar *)name, (xmlChar *)value); } noit_conf_mark_changed(); rv = 0; out: if(pobj) xmlXPathFreeObject(pobj); return rv; }
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; }
/** * 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; }