예제 #1
0
// sends the current chart definition
static inline void send_chart_definition(RRDSET *st) {
    rrdset_flag_set(st, RRDSET_FLAG_EXPOSED_UPSTREAM);

    buffer_sprintf(st->rrdhost->rrdpush_buffer, "CHART \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %ld %d \"%s %s %s\"\n"
                , st->id
                , st->name
                , st->title
                , st->units
                , st->family
                , st->context
                , rrdset_type_name(st->chart_type)
                , st->priority
                , st->update_every
                , rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)?"obsolete":""
                , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?"detail":""
                , rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)?"store_first":""
    );

    RRDDIM *rd;
    rrddim_foreach_read(rd, st) {
        buffer_sprintf(st->rrdhost->rrdpush_buffer, "DIMENSION \"%s\" \"%s\" \"%s\" " COLLECTED_NUMBER_FORMAT " " COLLECTED_NUMBER_FORMAT " \"%s %s\"\n"
                       , rd->id
                       , rd->name
                       , rrd_algorithm_name(rd->algorithm)
                       , rd->multiplier
                       , rd->divisor
                       , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?"hidden":""
                       , rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":""
        );
        rd->exposed = 1;
    }
예제 #2
0
파일: rrdvar.c 프로젝트: cppmx/netdata
static int single_variable2json(void *entry, void *data) {
    struct variable2json_helper *helper = (struct variable2json_helper *)data;
    RRDVAR *rv = (RRDVAR *)entry;
    calculated_number value = rrdvar2number(rv);

    if(unlikely(isnan(value) || isinf(value)))
        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name);
    else
        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5" LONG_DOUBLE_MODIFIER, helper->counter?",":"", rv->name, (LONG_DOUBLE)value);

    helper->counter++;

    return 0;
}
예제 #3
0
static inline int format_dimension_collected_graphite_plaintext(
          BUFFER *b                 // the buffer to write data to
        , const char *prefix        // the prefix to use
        , RRDHOST *host             // the host this chart comes from
        , const char *hostname      // the hostname (to override host->hostname)
        , RRDSET *st                // the chart
        , RRDDIM *rd                // the dimension
        , time_t after              // the start timestamp
        , time_t before             // the end timestamp
        , uint32_t options          // BACKEND_SOURCE_* bitmap
) {
    (void)host;
    (void)after;
    (void)before;
    (void)options;

    char chart_name[RRD_ID_LENGTH_MAX + 1];
    char dimension_name[RRD_ID_LENGTH_MAX + 1];
    backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX);
    backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX);

    buffer_sprintf(
            b
            , "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n"
            , prefix
            , hostname
            , chart_name
            , dimension_name
            , rd->last_collected_value
            , (uint32_t)rd->last_collected_time.tv_sec
    );

    return 1;
}
예제 #4
0
파일: registry.c 프로젝트: cppmx/netdata
// the main method for registering an access
int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
    if(unlikely(!registry.enabled))
        return registry_json_disabled(host, w, "access");

    // ------------------------------------------------------------------------
    // verify the browser supports cookies

    if(registry.verify_cookies_redirects > 0 && !person_guid[0]) {
        buffer_flush(w->response.data);
        registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
        w->response.data->contenttype = CT_APPLICATION_JSON;
        buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry.registry_to_announce);
        return 200;
    }

    if(unlikely(person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
        person_guid[0] = '\0';

    // ------------------------------------------------------------------------

    registry_lock();

    REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
    if(!p) {
        registry_json_header(host, w, "access", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 412;
    }

    // set the cookie
    registry_set_person_cookie(w, p);

    // generate the response
    registry_json_header(host, w, "access", REGISTRY_STATUS_OK);

    buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
    struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
    avl_traverse(&p->person_urls, registry_json_person_url_callback, &c);
    buffer_strcat(w->response.data, "\n\t]\n");

    registry_json_footer(w);
    registry_unlock();
    return 200;
}
예제 #5
0
파일: registry.c 프로젝트: cppmx/netdata
int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
    registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);

    buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
            registry.registry_to_announce);

    registry_json_footer(w);
    return 200;
}
예제 #6
0
파일: registry.c 프로젝트: cppmx/netdata
static inline int registry_json_disabled(RRDHOST *host, struct web_client *w, const char *action) {
    registry_json_header(host, w, action, REGISTRY_STATUS_DISABLED);

    buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
            registry.registry_to_announce);

    registry_json_footer(w);
    return 200;
}
예제 #7
0
파일: rrdvar.c 프로젝트: cppmx/netdata
void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
    RRDHOST *host = st->rrdhost;

    struct variable2json_helper helper = {
            .buf = buf,
            .counter = 0
    };

    buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context);
    avl_traverse_lock(&st->rrdvar_root_index, single_variable2json, (void *)&helper);
    buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family);
    helper.counter = 0;
    avl_traverse_lock(&st->rrdfamily->rrdvar_root_index, single_variable2json, (void *)&helper);
    buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", host->hostname);
    helper.counter = 0;
    avl_traverse_lock(&host->rrdvar_root_index, single_variable2json, (void *)&helper);
    buffer_strcat(buf, "\n\t}\n}\n");
}
예제 #8
0
파일: registry.c 프로젝트: cppmx/netdata
// callback for rendering PERSON_URLs
static int registry_json_person_url_callback(void *entry, void *data) {
    REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
    struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
    struct web_client *w = c->w;

    if(unlikely(c->count++))
        buffer_strcat(w->response.data, ",");

    buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
            pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);

    return 0;
}
예제 #9
0
파일: registry.c 프로젝트: cppmx/netdata
// callback for rendering MACHINE_URLs
static int registry_json_machine_url_callback(void *entry, void *data) {
    REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry;
    struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
    struct web_client *w = c->w;
    REGISTRY_MACHINE *m = c->m;

    if(unlikely(c->count++))
        buffer_strcat(w->response.data, ",");

    buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
            m->guid, mu->url->url, mu->last_t, mu->usages);

    return 1;
}
예제 #10
0
void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) {
    (void)format;

    char kq[2] = "",                    // key quote
            sq[2] = "";                     // string quote

    if( options & RRDR_OPTION_GOOGLE_JSON ) {
        kq[0] = '\0';
        sq[0] = '\'';
    }
    else {
        kq[0] = '"';
        sq[0] = '"';
    }

    if(string_value) buffer_strcat(wb, sq);

    buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
    buffer_rrd_value(wb, r->min);
    buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
    buffer_rrd_value(wb, r->max);
    buffer_strcat(wb, "\n}\n");
}
예제 #11
0
파일: web_api_old.c 프로젝트: cppmx/netdata
inline int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url) {
    (void)url;

    buffer_flush(w->response.data);
    RRDSET *st;

    rrdhost_rdlock(host);
    rrdset_foreach_read(st, host) {
        if(rrdset_is_available_for_viewers(st))
            buffer_sprintf(w->response.data, "%s\n", st->name);
    }
    rrdhost_unlock(host);

    return 200;
}
예제 #12
0
inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
    int ret = 400;
    char *chart = NULL;

    buffer_flush(w->response.data);

    while(url) {
        char *value = mystrsep(&url, "?&");
        if(!value || !*value) continue;

        char *name = mystrsep(&value, "=");
        if(!name || !*name) continue;
        if(!value || !*value) continue;

        // name and value are now the parameters
        // they are not null and not empty

        if(!strcmp(name, "chart")) chart = value;
        //else {
        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
        //  goto cleanup;
        //}
    }

    if(!chart || !*chart) {
        buffer_sprintf(w->response.data, "No chart id is given at the request.");
        goto cleanup;
    }

    RRDSET *st = rrdset_find(host, chart);
    if(!st) st = rrdset_find_byname(host, chart);
    if(!st) {
        buffer_strcat(w->response.data, "Chart is not found: ");
        buffer_strcat_htmlescape(w->response.data, chart);
        ret = 404;
        goto cleanup;
    }

    w->response.data->contenttype = CT_APPLICATION_JSON;
    st->last_accessed_time = now_realtime_sec();
    callback(st, w->response.data);
    return 200;

    cleanup:
    return ret;
}
예제 #13
0
static inline int format_dimension_stored_opentsdb_telnet(
          BUFFER *b                 // the buffer to write data to
        , const char *prefix        // the prefix to use
        , RRDHOST *host             // the host this chart comes from
        , const char *hostname      // the hostname (to override host->hostname)
        , RRDSET *st                // the chart
        , RRDDIM *rd                // the dimension
        , time_t after              // the start timestamp
        , time_t before             // the end timestamp
        , uint32_t options          // BACKEND_SOURCE_* bitmap
) {
    (void)host;

    time_t first_t = after, last_t = before;
    calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t);

    char chart_name[RRD_ID_LENGTH_MAX + 1];
    char dimension_name[RRD_ID_LENGTH_MAX + 1];
    backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX);
    backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX);

    if(!isnan(value)) {

        buffer_sprintf(
                b
                , "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s%s%s\n"
                , prefix
                , chart_name
                , dimension_name
                , (uint32_t) last_t
                , value
                , hostname
                , (host->tags)?" ":""
                , (host->tags)?host->tags:""
        );

        return 1;
    }
    return 0;
}
예제 #14
0
inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
    static int initialized = 0;
    int i;

    if(unlikely(initialized == 0)) {
        initialized = 1;

        for(i = 0; api_commands[i].command ; i++)
            api_commands[i].hash = simple_hash(api_commands[i].command);
    }

    // get the command
    char *tok = mystrsep(&url, "/?&");
    if(tok && *tok) {
        debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
        uint32_t hash = simple_hash(tok);

        for(i = 0; api_commands[i].command ;i++) {
            if(unlikely(hash == api_commands[i].hash && !strcmp(tok, api_commands[i].command))) {
                if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) && !(w->acl & api_commands[i].acl))
                    return web_client_permission_denied(w);

                return api_commands[i].callback(host, w, url);
            }
        }

        buffer_flush(w->response.data);
        buffer_strcat(w->response.data, "Unsupported v1 API command: ");
        buffer_strcat_htmlescape(w->response.data, tok);
        return 404;
    }
    else {
        buffer_flush(w->response.data);
        buffer_sprintf(w->response.data, "Which API v1 command?");
        return 400;
    }
}
예제 #15
0
inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url) {
    static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
            hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
            hash_to = 0 /*, hash_redirects = 0 */;

    if(unlikely(!hash_action)) {
        hash_action = simple_hash("action");
        hash_access = simple_hash("access");
        hash_hello = simple_hash("hello");
        hash_delete = simple_hash("delete");
        hash_search = simple_hash("search");
        hash_switch = simple_hash("switch");
        hash_machine = simple_hash("machine");
        hash_url = simple_hash("url");
        hash_name = simple_hash("name");
        hash_delete_url = simple_hash("delete_url");
        hash_for = simple_hash("for");
        hash_to = simple_hash("to");
/*
        hash_redirects = simple_hash("redirects");
*/
    }

    char person_guid[GUID_LEN + 1] = "";

    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);

    // TODO
    // The browser may send multiple cookies with our id

    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
    if(cookie)
        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);

    char action = '\0';
    char *machine_guid = NULL,
            *machine_url = NULL,
            *url_name = NULL,
            *search_machine_guid = NULL,
            *delete_url = NULL,
            *to_person_guid = NULL;
/*
    int redirects = 0;
*/

    while(url) {
        char *value = mystrsep(&url, "?&");
        if (!value || !*value) continue;

        char *name = mystrsep(&value, "=");
        if (!name || !*name) continue;
        if (!value || !*value) continue;

        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);

        uint32_t hash = simple_hash(name);

        if(hash == hash_action && !strcmp(name, "action")) {
            uint32_t vhash = simple_hash(value);

            if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
            else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
            else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
            else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
            else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
#ifdef NETDATA_INTERNAL_CHECKS
            else error("unknown registry action '%s'", value);
#endif /* NETDATA_INTERNAL_CHECKS */
        }
/*
        else if(hash == hash_redirects && !strcmp(name, "redirects"))
            redirects = atoi(value);
*/
        else if(hash == hash_machine && !strcmp(name, "machine"))
            machine_guid = value;

        else if(hash == hash_url && !strcmp(name, "url"))
            machine_url = value;

        else if(action == 'A') {
            if(hash == hash_name && !strcmp(name, "name"))
                url_name = value;
        }
        else if(action == 'D') {
            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
                delete_url = value;
        }
        else if(action == 'S') {
            if(hash == hash_for && !strcmp(name, "for"))
                search_machine_guid = value;
        }
        else if(action == 'W') {
            if(hash == hash_to && !strcmp(name, "to"))
                to_person_guid = value;
        }
#ifdef NETDATA_INTERNAL_CHECKS
        else error("unused registry URL parameter '%s' with value '%s'", name, value);
#endif /* NETDATA_INTERNAL_CHECKS */
    }

    if(unlikely(respect_web_browser_do_not_track_policy && web_client_has_donottrack(w))) {
        buffer_flush(w->response.data);
        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
        return 400;
    }

    if(unlikely(action == 'H')) {
        // HELLO request, dashboard ACL
        if(unlikely(!web_client_can_access_dashboard(w)))
            return web_client_permission_denied(w);
    }
    else {
        // everything else, registry ACL
        if(unlikely(!web_client_can_access_registry(w)))
            return web_client_permission_denied(w);
    }

    switch(action) {
        case 'A':
            if(unlikely(!machine_guid || !machine_url || !url_name)) {
                error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')", machine_guid ? machine_guid : "UNSET", machine_url ? machine_url : "UNSET", url_name ? url_name : "UNSET");
                buffer_flush(w->response.data);
                buffer_strcat(w->response.data, "Invalid registry Access request.");
                return 400;
            }

            web_client_enable_tracking_required(w);
            return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());

        case 'D':
            if(unlikely(!machine_guid || !machine_url || !delete_url)) {
                error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')", machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
                buffer_flush(w->response.data);
                buffer_strcat(w->response.data, "Invalid registry Delete request.");
                return 400;
            }

            web_client_enable_tracking_required(w);
            return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());

        case 'S':
            if(unlikely(!machine_guid || !machine_url || !search_machine_guid)) {
                error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')", machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
                buffer_flush(w->response.data);
                buffer_strcat(w->response.data, "Invalid registry Search request.");
                return 400;
            }

            web_client_enable_tracking_required(w);
            return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());

        case 'W':
            if(unlikely(!machine_guid || !machine_url || !to_person_guid)) {
                error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')", machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
                buffer_flush(w->response.data);
                buffer_strcat(w->response.data, "Invalid registry Switch request.");
                return 400;
            }

            web_client_enable_tracking_required(w);
            return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());

        case 'H':
            return registry_request_hello_json(host, w);

        default:
            buffer_flush(w->response.data);
            buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
            return 400;
    }
}
예제 #16
0
파일: registry.c 프로젝트: cppmx/netdata
// the main method for switching user identity
int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
    if(!registry.enabled)
        return registry_json_disabled(host, w, "switch");

    (void)url;
    (void)when;

    registry_lock();

    REGISTRY_PERSON *op = registry_person_find(person_guid);
    if(!op) {
        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 430;
    }

    REGISTRY_PERSON *np = registry_person_find(new_person_guid);
    if(!np) {
        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 431;
    }

    REGISTRY_MACHINE *m = registry_machine_find(machine_guid);
    if(!m) {
        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 432;
    }

    struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };

    // verify the old person has access to this machine
    avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
    if(!data.count) {
        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 433;
    }

    // verify the new person has access to this machine
    data.count = 0;
    avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
    if(!data.count) {
        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
        registry_json_footer(w);
        registry_unlock();
        return 434;
    }

    // set the cookie of the new person
    // the user just switched identity
    registry_set_person_cookie(w, np);

    // generate the response
    registry_json_header(host, w, "switch", REGISTRY_STATUS_OK);
    buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
    registry_json_footer(w);

    registry_unlock();
    return 200;
}
예제 #17
0
void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value) {
    rrdset_check_rdlock(r->st);

    long rows = rrdr_rows(r);
    long c, i;
    RRDDIM *rd;

    //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
    char kq[2] = "",                    // key quote
            sq[2] = "";                     // string quote

    if( options & RRDR_OPTION_GOOGLE_JSON ) {
        kq[0] = '\0';
        sq[0] = '\'';
    }
    else {
        kq[0] = '"';
        sq[0] = '"';
    }

    buffer_sprintf(wb, "{\n"
                       "   %sapi%s: 1,\n"
                       "   %sid%s: %s%s%s,\n"
                       "   %sname%s: %s%s%s,\n"
                       "   %sview_update_every%s: %d,\n"
                       "   %supdate_every%s: %d,\n"
                       "   %sfirst_entry%s: %u,\n"
                       "   %slast_entry%s: %u,\n"
                       "   %sbefore%s: %u,\n"
                       "   %safter%s: %u,\n"
                       "   %sdimension_names%s: ["
                   , kq, kq
                   , kq, kq, sq, r->st->id, sq
                   , kq, kq, sq, r->st->name, sq
                   , kq, kq, r->update_every
                   , kq, kq, r->st->update_every
                   , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
                   , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
                   , kq, kq, (uint32_t)r->before
                   , kq, kq, (uint32_t)r->after
                   , kq, kq);

    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
        if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;

        if(i) buffer_strcat(wb, ", ");
        buffer_strcat(wb, sq);
        buffer_strcat(wb, rd->name);
        buffer_strcat(wb, sq);
        i++;
    }
    if(!i) {
#ifdef NETDATA_INTERNAL_CHECKS
        error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
#endif
        rows = 0;
        buffer_strcat(wb, sq);
        buffer_strcat(wb, "no data");
        buffer_strcat(wb, sq);
    }

    buffer_sprintf(wb, "],\n"
                       "   %sdimension_ids%s: ["
                   , kq, kq);

    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
        if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;

        if(i) buffer_strcat(wb, ", ");
        buffer_strcat(wb, sq);
        buffer_strcat(wb, rd->id);
        buffer_strcat(wb, sq);
        i++;
    }
    if(!i) {
        rows = 0;
        buffer_strcat(wb, sq);
        buffer_strcat(wb, "no data");
        buffer_strcat(wb, sq);
    }

    buffer_sprintf(wb, "],\n"
                       "   %slatest_values%s: ["
                   , kq, kq);

    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
        if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;

        if(i) buffer_strcat(wb, ", ");
        i++;

        storage_number n = rd->values[rrdset_last_slot(r->st)];

        if(!does_storage_number_exist(n))
            buffer_strcat(wb, "null");
        else
            buffer_rrd_value(wb, unpack_storage_number(n));
    }
    if(!i) {
        rows = 0;
        buffer_strcat(wb, "null");
    }

    buffer_sprintf(wb, "],\n"
                       "   %sview_latest_values%s: ["
                   , kq, kq);

    i = 0;
    if(rows) {
        calculated_number total = 1;

        if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
            total = 0;
            for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
                calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
                calculated_number n = cn[c];

                if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
                    n = -n;

                total += n;
            }
            // prevent a division by zero
            if(total == 0) total = 1;
        }

        for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
            if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
            if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;

            if(i) buffer_strcat(wb, ", ");
            i++;

            calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
            RRDR_VALUE_FLAGS *co = &r->o[ (rrdr_rows(r) - 1) * r->d ];
            calculated_number n = cn[c];

            if(co[c] & RRDR_VALUE_EMPTY) {
                if(options & RRDR_OPTION_NULL2ZERO)
                    buffer_strcat(wb, "0");
                else
                    buffer_strcat(wb, "null");
            }
            else {
                if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
                    n = -n;

                if(unlikely(options & RRDR_OPTION_PERCENTAGE))
                    n = n * 100 / total;

                buffer_rrd_value(wb, n);
            }
        }
    }
    if(!i) {
        rows = 0;
        buffer_strcat(wb, "null");
    }

    buffer_sprintf(wb, "],\n"
                       "   %sdimensions%s: %ld,\n"
                       "   %spoints%s: %ld,\n"
                       "   %sformat%s: %s"
                   , kq, kq, i
                   , kq, kq, rows
                   , kq, kq, sq
    );

    rrdr_buffer_print_format(wb, format);

    buffer_sprintf(wb, "%s,\n"
                       "   %sresult%s: "
                   , sq
                   , kq, kq
    );

    if(string_value) buffer_strcat(wb, sq);
    //info("JSONWRAPPER(): %s: END", r->st->id);
}
예제 #18
0
// returns the HTTP code
inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);

    int ret = 400;
    BUFFER *dimensions = NULL;

    buffer_flush(w->response.data);

    char    *google_version = "0.6",
            *google_reqId = "0",
            *google_sig = "0",
            *google_out = "json",
            *responseHandler = NULL,
            *outFileName = NULL;

    time_t last_timestamp_in_data = 0, google_timestamp = 0;

    char *chart = NULL
    , *before_str = NULL
    , *after_str = NULL
    , *group_time_str = NULL
    , *points_str = NULL;

    int group = RRDR_GROUPING_AVERAGE;
    uint32_t format = DATASOURCE_JSON;
    uint32_t options = 0x00000000;

    while(url) {
        char *value = mystrsep(&url, "?&");
        if(!value || !*value) continue;

        char *name = mystrsep(&value, "=");
        if(!name || !*name) continue;
        if(!value || !*value) continue;

        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);

        // name and value are now the parameters
        // they are not null and not empty

        if(!strcmp(name, "chart")) chart = value;
        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
            if(!dimensions) dimensions = buffer_create(100);
            buffer_strcat(dimensions, "|");
            buffer_strcat(dimensions, value);
        }
        else if(!strcmp(name, "after")) after_str = value;
        else if(!strcmp(name, "before")) before_str = value;
        else if(!strcmp(name, "points")) points_str = value;
        else if(!strcmp(name, "gtime")) group_time_str = value;
        else if(!strcmp(name, "group")) {
            group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
        }
        else if(!strcmp(name, "format")) {
            format = web_client_api_request_v1_data_format(value);
        }
        else if(!strcmp(name, "options")) {
            options |= web_client_api_request_v1_data_options(value);
        }
        else if(!strcmp(name, "callback")) {
            responseHandler = value;
        }
        else if(!strcmp(name, "filename")) {
            outFileName = value;
        }
        else if(!strcmp(name, "tqx")) {
            // parse Google Visualization API options
            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
            char *tqx_name, *tqx_value;

            while(value) {
                tqx_value = mystrsep(&value, ";");
                if(!tqx_value || !*tqx_value) continue;

                tqx_name = mystrsep(&tqx_value, ":");
                if(!tqx_name || !*tqx_name) continue;
                if(!tqx_value || !*tqx_value) continue;

                if(!strcmp(tqx_name, "version"))
                    google_version = tqx_value;
                else if(!strcmp(tqx_name, "reqId"))
                    google_reqId = tqx_value;
                else if(!strcmp(tqx_name, "sig")) {
                    google_sig = tqx_value;
                    google_timestamp = strtoul(google_sig, NULL, 0);
                }
                else if(!strcmp(tqx_name, "out")) {
                    google_out = tqx_value;
                    format = web_client_api_request_v1_data_google_format(google_out);
                }
                else if(!strcmp(tqx_name, "responseHandler"))
                    responseHandler = tqx_value;
                else if(!strcmp(tqx_name, "outFileName"))
                    outFileName = tqx_value;
            }
        }
    }

    if(!chart || !*chart) {
        buffer_sprintf(w->response.data, "No chart id is given at the request.");
        goto cleanup;
    }

    RRDSET *st = rrdset_find(host, chart);
    if(!st) st = rrdset_find_byname(host, chart);
    if(!st) {
        buffer_strcat(w->response.data, "Chart is not found: ");
        buffer_strcat_htmlescape(w->response.data, chart);
        ret = 404;
        goto cleanup;
    }
    st->last_accessed_time = now_realtime_sec();

    long long before = (before_str && *before_str)?str2l(before_str):0;
    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
    int       points = (points_str && *points_str)?str2i(points_str):0;
    long      group_time = (group_time_str && *group_time_str)?str2l(group_time_str):0;

    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
          , w->id
          , chart
          , (dimensions)?buffer_tostring(dimensions):""
          , after
          , before
          , points
          , group
          , format
          , options
    );

    if(outFileName && *outFileName) {
        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
    }

    if(format == DATASOURCE_DATATABLE_JSONP) {
        if(responseHandler == NULL)
            responseHandler = "google.visualization.Query.setResponse";

        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
        );

        buffer_sprintf(w->response.data,
                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
                responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
    }
    else if(format == DATASOURCE_JSONP) {
        if(responseHandler == NULL)
            responseHandler = "callback";

        buffer_strcat(w->response.data, responseHandler);
        buffer_strcat(w->response.data, "(");
    }

    ret = rrdset2anything_api_v1(st, w->response.data, dimensions, format, points, after, before, group, group_time
                                 , options, &last_timestamp_in_data);

    if(format == DATASOURCE_DATATABLE_JSONP) {
        if(google_timestamp < last_timestamp_in_data)
            buffer_strcat(w->response.data, "});");

        else {
            // the client already has the latest data
            buffer_flush(w->response.data);
            buffer_sprintf(w->response.data,
                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
                    responseHandler, google_version, google_reqId);
        }
    }
    else if(format == DATASOURCE_JSONP)
        buffer_strcat(w->response.data, ");");

    cleanup:
    buffer_free(dimensions);
    return ret;
}
예제 #19
0
static inline int format_dimension_collected_json_plaintext(
          BUFFER *b                 // the buffer to write data to
        , const char *prefix        // the prefix to use
        , RRDHOST *host             // the host this chart comes from
        , const char *hostname      // the hostname (to override host->hostname)
        , RRDSET *st                // the chart
        , RRDDIM *rd                // the dimension
        , time_t after              // the start timestamp
        , time_t before             // the end timestamp
        , uint32_t options          // BACKEND_SOURCE_* bitmap
) {
    (void)host;
    (void)after;
    (void)before;
    (void)options;

    const char *tags_pre = "", *tags_post = "", *tags = host->tags;
    if(!tags) tags = "";

    if(*tags) {
        if(*tags == '{' || *tags == '[' || *tags == '"') {
            tags_pre = "\"host_tags\":";
            tags_post = ",";
        }
        else {
            tags_pre = "\"host_tags\":\"";
            tags_post = "\",";
        }
    }

    buffer_sprintf(b, "{"
        "\"prefix\":\"%s\","
        "\"hostname\":\"%s\","
        "%s%s%s"

        "\"chart_id\":\"%s\","
        "\"chart_name\":\"%s\","
        "\"chart_family\":\"%s\","
        "\"chart_context\": \"%s\","
        "\"chart_type\":\"%s\","
        "\"units\": \"%s\","

        "\"id\":\"%s\","
        "\"name\":\"%s\","
        "\"value\":" COLLECTED_NUMBER_FORMAT ","

        "\"timestamp\": %u}\n",
            prefix,
            hostname,
            tags_pre, tags, tags_post,

            st->id,
            st->name,
            st->family,
            st->context,
            st->type,
            st->units,

            rd->id,
            rd->name,
            rd->last_collected_value,

            (uint32_t)rd->last_collected_time.tv_sec
    );

    return 1;
}
예제 #20
0
void charts2json(RRDHOST *host, BUFFER *wb) {
    static char *custom_dashboard_info_js_filename = NULL;
    size_t c, dimensions = 0, memory = 0, alarms = 0;
    RRDSET *st;

    time_t now = now_realtime_sec();

    if(unlikely(!custom_dashboard_info_js_filename))
        custom_dashboard_info_js_filename = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", "");

    buffer_sprintf(wb, "{\n"
                       "\t\"hostname\": \"%s\""
                       ",\n\t\"version\": \"%s\""
                       ",\n\t\"os\": \"%s\""
                       ",\n\t\"timezone\": \"%s\""
                       ",\n\t\"update_every\": %d"
                       ",\n\t\"history\": %ld"
                       ",\n\t\"custom_info\": \"%s\""
                       ",\n\t\"charts\": {"
                   , host->hostname
                   , host->program_version
                   , host->os
                   , host->timezone
                   , host->rrd_update_every
                   , host->rrd_history_entries
                   , custom_dashboard_info_js_filename
    );

    c = 0;
    rrdhost_rdlock(host);
    rrdset_foreach_read(st, host) {
        if(rrdset_is_available_for_viewers(st)) {
            if(c) buffer_strcat(wb, ",");
            buffer_strcat(wb, "\n\t\t\"");
            buffer_strcat(wb, st->id);
            buffer_strcat(wb, "\": ");
            rrdset2json(st, wb, &dimensions, &memory);

            c++;
            st->last_accessed_time = now;
        }
    }

    RRDCALC *rc;
    for(rc = host->alarms; rc ; rc = rc->next) {
        if(rc->rrdset)
            alarms++;
    }
    rrdhost_unlock(host);

    buffer_sprintf(wb
                   , "\n\t}"
                     ",\n\t\"charts_count\": %zu"
                     ",\n\t\"dimensions_count\": %zu"
                     ",\n\t\"alarms_count\": %zu"
                     ",\n\t\"rrd_memory_bytes\": %zu"
                     ",\n\t\"hosts_count\": %zu"
                     ",\n\t\"hosts\": ["
                   , c
                   , dimensions
                   , alarms
                   , memory
                   , rrd_hosts_available
    );

    if(unlikely(rrd_hosts_available > 1)) {
        rrd_rdlock();

        size_t found = 0;
        RRDHOST *h;
        rrdhost_foreach_read(h) {
            if(!rrdhost_should_be_removed(h, host, now)) {
                buffer_sprintf(wb
                               , "%s\n\t\t{"
                                 "\n\t\t\t\"hostname\": \"%s\""
                                 "\n\t\t}"
                               , (found > 0) ? "," : ""
                               , h->hostname
                );

                found++;
            }
        }

        rrd_unlock();
    }
예제 #21
0
static inline int format_dimension_stored_json_plaintext(
          BUFFER *b                 // the buffer to write data to
        , const char *prefix        // the prefix to use
        , RRDHOST *host             // the host this chart comes from
        , const char *hostname      // the hostname (to override host->hostname)
        , RRDSET *st                // the chart
        , RRDDIM *rd                // the dimension
        , time_t after              // the start timestamp
        , time_t before             // the end timestamp
        , uint32_t options          // BACKEND_SOURCE_* bitmap
) {
    (void)host;

    time_t first_t = after, last_t = before;
    calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t);

    if(!isnan(value)) {
        const char *tags_pre = "", *tags_post = "", *tags = host->tags;
        if(!tags) tags = "";

        if(*tags) {
            if(*tags == '{' || *tags == '[' || *tags == '"') {
                tags_pre = "\"host_tags\":";
                tags_post = ",";
            }
            else {
                tags_pre = "\"host_tags\":\"";
                tags_post = "\",";
            }
        }

        buffer_sprintf(b, "{"
            "\"prefix\":\"%s\","
            "\"hostname\":\"%s\","
            "%s%s%s"

            "\"chart_id\":\"%s\","
            "\"chart_name\":\"%s\","
            "\"chart_family\":\"%s\","
            "\"chart_context\": \"%s\","
            "\"chart_type\":\"%s\","
            "\"units\": \"%s\","

            "\"id\":\"%s\","
            "\"name\":\"%s\","
            "\"value\":" CALCULATED_NUMBER_FORMAT ","

            "\"timestamp\": %u}\n", 
                prefix,
                hostname,
                tags_pre, tags, tags_post,

                st->id,
                st->name,
                st->family,
                st->context,
                st->type,
                st->units,

                rd->id,
                rd->name,
                value, 
                
                (uint32_t) last_t
        );
        
        return 1;
    }
    return 0;
}
예제 #22
0
파일: web_api_old.c 프로젝트: cppmx/netdata
int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type) {
    if(!url || !*url) {
        buffer_flush(w->response.data);
        buffer_sprintf(w->response.data, "Incomplete request.");
        return 400;
    }

    RRDSET *st = NULL;

    char *args = strchr(url, '?');
    if(args) {
        *args='\0';
        args = &args[1];
    }

    // get the name of the data to show
    char *tok = mystrsep(&url, "/");
    if(!tok) tok = "";

    // do we have such a data set?
    if(*tok) {
        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
        st = rrdset_find_byname(host, tok);
        if(!st) st = rrdset_find(host, tok);
    }

    if(!st) {
        // we don't have it
        // try to send a file with that name
        buffer_flush(w->response.data);
        return(mysendfile(w, tok));
    }

    // we have it
    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);

    // how many entries does the client want?
    int lines = (int)st->entries;
    int group_count = 1;
    time_t after = 0, before = 0;
    int group_method = GROUP_AVERAGE;
    int nonzero = 0;

    if(url) {
        // parse the lines required
        tok = mystrsep(&url, "/");
        if(tok) lines = str2i(tok);
        if(lines < 1) lines = 1;
    }
    if(url) {
        // parse the group count required
        tok = mystrsep(&url, "/");
        if(tok && *tok) group_count = str2i(tok);
        if(group_count < 1) group_count = 1;
        //if(group_count > save_history / 20) group_count = save_history / 20;
    }
    if(url) {
        // parse the grouping method required
        tok = mystrsep(&url, "/");
        if(tok && *tok) {
            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
        }
    }
    if(url) {
        // parse after time
        tok = mystrsep(&url, "/");
        if(tok && *tok) after = str2ul(tok);
        if(after < 0) after = 0;
    }
    if(url) {
        // parse before time
        tok = mystrsep(&url, "/");
        if(tok && *tok) before = str2ul(tok);
        if(before < 0) before = 0;
    }
    if(url) {
        // parse nonzero
        tok = mystrsep(&url, "/");
        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
    }

    w->response.data->contenttype = CT_APPLICATION_JSON;
    buffer_flush(w->response.data);

    char *google_version = "0.6";
    char *google_reqId = "0";
    char *google_sig = "0";
    char *google_out = "json";
    char *google_responseHandler = "google.visualization.Query.setResponse";
    char *google_outFileName = NULL;
    time_t last_timestamp_in_data = 0;
    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {

        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;

        while(args) {
            tok = mystrsep(&args, "&");
            if(tok && *tok) {
                char *name = mystrsep(&tok, "=");
                if(name && *name && strcmp(name, "tqx") == 0) {
                    char *key = mystrsep(&tok, ":");
                    char *value = mystrsep(&tok, ";");
                    if(key && value && *key && *value) {
                        if(strcmp(key, "version") == 0)
                            google_version = value;

                        else if(strcmp(key, "reqId") == 0)
                            google_reqId = value;

                        else if(strcmp(key, "sig") == 0)
                            google_sig = value;

                        else if(strcmp(key, "out") == 0)
                            google_out = value;

                        else if(strcmp(key, "responseHandler") == 0)
                            google_responseHandler = value;

                        else if(strcmp(key, "outFileName") == 0)
                            google_outFileName = value;
                    }
                }
            }
        }

        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
                w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
        );

        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
            last_timestamp_in_data = strtoul(google_sig, NULL, 0);

            // check the client wants json
            if(strcmp(google_out, "json") != 0) {
                buffer_sprintf(w->response.data,
                        "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
                        google_responseHandler, google_version, google_reqId, google_out);
                return 200;
            }
        }
    }

    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
        buffer_sprintf(w->response.data,
                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
                google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
    }

    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
            w->id, st->name, st->id, lines, group_count, group_method, after, before);

    time_t timestamp_in_data = rrdset2json_api_old(datasource_type, st, w->response.data, lines, group_count
                                                   , group_method, (unsigned long) after, (unsigned long) before
                                                   , nonzero);

    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
        if(timestamp_in_data > last_timestamp_in_data)
            buffer_strcat(w->response.data, "});");

        else {
            // the client already has the latest data
            buffer_flush(w->response.data);
            buffer_sprintf(w->response.data,
                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
                    google_responseHandler, google_version, google_reqId);
        }
    }

    return 200;
}
예제 #23
0
void generate_config(BUFFER *wb, int only_changed)
{
	int i, pri;
	struct config *co;
	struct config_value *cv;

	for(i = 0; i < 3 ;i++) {
		switch(i) {
			case 0:
				buffer_strcat(wb,
					"# NetData Configuration\n"
					"# You can uncomment and change any of the options below.\n"
					"# The value shown in the commented settings, is the default value.\n"
					"\n# global netdata configuration\n");
				break;

			case 1:
				buffer_strcat(wb, "\n\n# per plugin configuration\n");
				break;

			case 2:
				buffer_strcat(wb, "\n\n# per chart configuration\n");
				break;
		}

		pthread_rwlock_wrlock(&config_rwlock);
		for(co = config_root; co ; co = co->next) {
			if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0) pri = 0;
			else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
			else pri = 2;

			if(i == pri) {
				int used = 0;
				int changed = 0;
				int count = 0;

				pthread_rwlock_wrlock(&co->rwlock);

				for(cv = co->values; cv ; cv = cv->next) {
					used += (cv->flags && CONFIG_VALUE_USED)?1:0;
					changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
					count++;
				}

				pthread_rwlock_unlock(&co->rwlock);

				if(!count) continue;
				if(only_changed && !changed) continue;

				if(!used) {
					buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
				}

				buffer_sprintf(wb, "\n[%s]\n", co->name);

				pthread_rwlock_wrlock(&co->rwlock);
				for(cv = co->values; cv ; cv = cv->next) {

					if(used && !(cv->flags & CONFIG_VALUE_USED)) {
						buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
					}
					buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
				}
				pthread_rwlock_unlock(&co->rwlock);
			}
		}
		pthread_rwlock_unlock(&config_rwlock);
	}
}
예제 #24
0
int
main(void)
{
    struct buffer one = { 0, 0, 0, NULL };
    struct buffer two = { 0, 0, 0, NULL };
    struct buffer *three;
    int fd;
    char *data;
    ssize_t count;
    size_t offset;

    plan(89);

    /* buffer_set, buffer_append, buffer_swap */
    buffer_set(&one, test_string1, sizeof(test_string1));
    is_int(1024, one.size, "minimum size is 1024");
    is_int(0, one.used, "used starts at 0");
    is_int(sizeof(test_string1), one.left, "left is correct");
    is_string(test_string1, one.data, "data is corect");
    buffer_append(&one, test_string2, sizeof(test_string2));
    is_int(1024, one.size, "appended data doesn't change size");
    is_int(0, one.used, "or used");
    is_int(sizeof(test_string3), one.left, "but left is the right size");
    ok(memcmp(one.data, test_string3, sizeof(test_string3)) == 0,
       "and the resulting data is correct");
    one.left -= sizeof(test_string1);
    one.used += sizeof(test_string1);
    buffer_append(&one, test_string1, sizeof(test_string1));
    is_int(1024, one.size, "size still isn't larger after adding data");
    is_int(sizeof(test_string1), one.used, "and used is preserved on append");
    is_int(sizeof(test_string3), one.left, "and left is updated properly");
    ok(memcmp(one.data + one.used, test_string2, sizeof(test_string2)) == 0,
       "and the middle data is unchanged");
    ok(memcmp(one.data + one.used + sizeof(test_string2), test_string1,
              sizeof(test_string1)) == 0, "and the final data is correct");
    buffer_set(&one, test_string1, sizeof(test_string1));
    buffer_set(&two, test_string2, sizeof(test_string2));
    buffer_swap(&one, &two);
    is_int(1024, one.size, "swap #1 size is correct");
    is_int(0, one.used, "swap #1 used is correct");
    is_int(sizeof(test_string2), one.left, "swap #1 left is correct");
    is_string(test_string2, one.data, "swap #1 data is correct");
    is_int(1024, two.size, "swap #2 size is correct");
    is_int(0, two.used, "swap #2 used is correct");
    is_int(sizeof(test_string1), two.left, "swap #2 left is correct");
    is_string(test_string1, two.data, "swap #2 data is correct");
    free(one.data);
    free(two.data);
    one.data = NULL;
    two.data = NULL;
    one.size = 0;
    two.size = 0;

    /* buffer_resize */
    three = buffer_new();
    ok(three != NULL, "buffer_new works");
    if (three == NULL)
        bail("buffer_new returned NULL");
    is_int(0, three->size, "initial size is 0");
    buffer_set(three, test_string1, sizeof(test_string1));
    is_int(1024, three->size, "size becomes 1024 when adding data");
    buffer_resize(three, 512);
    is_int(1024, three->size, "resizing to something smaller doesn't change");
    buffer_resize(three, 1025);
    is_int(2048, three->size, "resizing to something larger goes to 2048");
    buffer_free(three);

    /* buffer_read, buffer_find_string, buffer_compact */
    fd = open("buffer-test", O_RDWR | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
        sysbail("cannot create buffer-test");
    data = bmalloc(2048);
    memset(data, 'a', 1023);
    data[1023] = '\r';
    data[1024] = '\n';
    memset(data + 1025, 'b', 1023);
    if (xwrite(fd, data, 2048) < 2048)
        sysbail("cannot write to buffer-test");
    if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
        sysbail("cannot rewind buffer-test");
    three = buffer_new();
    ok(three != NULL, "buffer_new works");
    if (three == NULL)
        bail("buffer_new returned NULL");
    is_int(0, three->size, "and initial size is 0");
    buffer_resize(three, 1024);
    is_int(1024, three->size, "resize to 1024 works");
    count = buffer_read(three, fd);
    is_int(1024, count, "reading into a buffer of size 1024 reads 1024");
    offset = 0;
    ok(!buffer_find_string(three, "\r\n", 0, &offset),
       "buffer_find_string with truncated string fails");
    is_int(0, offset, "and offset is unchanged");
    ok(memcmp(three->data, data, three->size) == 0, "buffer data is correct");
    buffer_resize(three, 2048);
    is_int(2048, three->size, "resizing the buffer to 2048 works");
    count = buffer_read(three, fd);
    is_int(1024, count, "and now we can read the rest of the data");
    ok(memcmp(three->data, data, 2048) == 0, "and it's all there");
    ok(!buffer_find_string(three, "\r\n", 1024, &offset),
       "buffer_find_string with a string starting before offset fails");
    is_int(0, offset, "and offset is unchanged");
    ok(buffer_find_string(three, "\r\n", 0, &offset),
       "finding the string on the whole buffer works");
    is_int(1023, offset, "and returns the correct location");
    three->used += 400;
    three->left -= 400;
    buffer_compact(three);
    is_int(2048, three->size, "compacting buffer doesn't change the size");
    is_int(0, three->used, "but used is now zero");
    is_int(1648, three->left, "and left is decreased appropriately");
    ok(memcmp(three->data, data + 400, 1648) == 0, "and the data is correct");
    count = buffer_read(three, fd);
    is_int(0, count, "reading at EOF returns 0");
    close(fd);
    unlink("buffer-test");
    free(data);
    buffer_free(three);

    /* buffer_sprintf and buffer_append_sprintf */
    three = buffer_new();
    buffer_append_sprintf(three, "testing %d testing", 6);
    is_int(0, three->used, "buffer_append_sprintf doesn't change used");
    is_int(17, three->left, "but sets left correctly");
    buffer_append(three, "", 1);
    is_int(18, three->left, "appending a nul works");
    is_string("testing 6 testing", three->data, "and the data is correct");
    three->left--;
    three->used += 5;
    three->left -= 5;
    buffer_append_sprintf(three, " %d", 7);
    is_int(14, three->left, "appending a digit works");
    buffer_append(three, "", 1);
    is_string("testing 6 testing 7", three->data, "and the data is correct");
    buffer_sprintf(three, "%d testing", 8);
    is_int(9, three->left, "replacing the buffer works");
    is_string("8 testing", three->data, "and the results are correct");
    data = bmalloc(1050);
    memset(data, 'a', 1049);
    data[1049] = '\0';
    is_int(1024, three->size, "size before large sprintf is 1024");
    buffer_sprintf(three, "%s", data);
    is_int(2048, three->size, "size after large sprintf is 2048");
    is_int(1049, three->left, "and left is correct");
    buffer_append(three, "", 1);
    is_string(data, three->data, "and data is correct");
    free(data);
    buffer_free(three);

    /* buffer_read_all */
    fd = open("buffer-test", O_RDWR | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
        sysbail("cannot create buffer-test");
    data = bmalloc(2049);
    memset(data, 'a', 2049);
    if (xwrite(fd, data, 2049) < 2049)
        sysbail("cannot write to buffer-test");
    if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
        sysbail("cannot rewind buffer-test");
    three = buffer_new();
    ok(buffer_read_all(three, fd), "buffer_read_all succeeds");
    is_int(0, three->used, "and unused is zero");
    is_int(2049, three->left, "and left is correct");
    is_int(4096, three->size, "and size is correct");
    ok(memcmp(data, three->data, 2049) == 0, "and data is correct");
    if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
        sysbail("cannot rewind buffer-test");
    ok(buffer_read_all(three, fd), "reading again succeeds");
    is_int(0, three->used, "and used is correct");
    is_int(4098, three->left, "and left is now larger");
    is_int(8192, three->size, "and size doubled");
    ok(memcmp(data, three->data + 2049, 2049) == 0, "and data is correct");

    /* buffer_read_file */
    if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
        sysbail("cannot rewind buffer-test");
    buffer_free(three);
    three = buffer_new();
    ok(buffer_read_file(three, fd), "buffer_read_file succeeds");
    is_int(0, three->used, "and leaves unused at 0");
    is_int(2049, three->left, "and left is correct");
    is_int(3072, three->size, "and size is a multiple of 1024");
    ok(memcmp(data, three->data, 2049) == 0, "and the data is correct");

    /* buffer_read_all and buffer_read_file errors */
    close(fd);
    ok(!buffer_read_all(three, fd), "buffer_read_all on closed fd fails");
    is_int(3072, three->size, "and size is unchanged");
    ok(!buffer_read_file(three, fd), "buffer_read_file on closed fd fails");
    is_int(3072, three->size, "and size is unchanged");
    is_int(2049, three->left, "and left is unchanged");
    unlink("buffer-test");
    free(data);
    buffer_free(three);

    /* buffer_vsprintf and buffer_append_vsprintf */
    three = buffer_new();
    test_append_vsprintf(three, "testing %d testing", 6);
    is_int(0, three->used, "buffer_append_vsprintf leaves used as 0");
    is_int(17, three->left, "and left is correct");
    buffer_append(three, "", 1);
    is_int(18, three->left, "and left is correct after appending a nul");
    is_string("testing 6 testing", three->data, "and data is correct");
    three->left--;
    three->used += 5;
    three->left -= 5;
    test_append_vsprintf(three, " %d", 7);
    is_int(14, three->left, "and appending results in the correct left");
    buffer_append(three, "", 1);
    is_string("testing 6 testing 7", three->data, "and the right data");
    test_vsprintf(three, "%d testing", 8);
    is_int(9, three->left, "replacing the buffer results in the correct size");
    is_string("8 testing", three->data, "and the correct data");
    data = bmalloc(1050);
    memset(data, 'a', 1049);
    data[1049] = '\0';
    is_int(1024, three->size, "size is 1024 before large vsprintf");
    test_vsprintf(three, "%s", data);
    is_int(2048, three->size, "and 2048 afterwards");
    is_int(1049, three->left, "and left is correct");
    buffer_append(three, "", 1);
    is_string(data, three->data, "and data is correct");
    free(data);
    buffer_free(three);

    /* Test buffer_free with NULL and ensure it doesn't explode. */
    buffer_free(NULL);

    return 0;
}
예제 #25
0
unsigned long rrdset_info2json_api_old(RRDSET *st, char *options, BUFFER *wb) {
    time_t now = now_realtime_sec();

    rrdset_rdlock(st);

    st->last_accessed_time = now;

    buffer_sprintf(wb,
            "\t\t{\n"
            "\t\t\t\"id\": \"%s\",\n"
            "\t\t\t\"name\": \"%s\",\n"
            "\t\t\t\"type\": \"%s\",\n"
            "\t\t\t\"family\": \"%s\",\n"
            "\t\t\t\"context\": \"%s\",\n"
            "\t\t\t\"title\": \"%s\",\n"
            "\t\t\t\"priority\": %ld,\n"
            "\t\t\t\"enabled\": %d,\n"
            "\t\t\t\"units\": \"%s\",\n"
            "\t\t\t\"url\": \"/data/%s/%s\",\n"
            "\t\t\t\"chart_type\": \"%s\",\n"
            "\t\t\t\"counter\": %lu,\n"
            "\t\t\t\"entries\": %ld,\n"
            "\t\t\t\"first_entry_t\": %ld,\n"
            "\t\t\t\"last_entry\": %lu,\n"
            "\t\t\t\"last_entry_t\": %ld,\n"
            "\t\t\t\"last_entry_secs_ago\": %ld,\n"
            "\t\t\t\"update_every\": %d,\n"
            "\t\t\t\"isdetail\": %d,\n"
            "\t\t\t\"usec_since_last_update\": %llu,\n"
            "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
            "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
            "\t\t\t\"dimensions\": [\n"
            , st->id
            , st->name
            , st->type
            , st->family
            , st->context
            , st->title
            , st->priority
            , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?1:0
            , st->units
            , st->name, options?options:""
            , rrdset_type_name(st->chart_type)
            , st->counter
            , st->entries
            , rrdset_first_entry_t(st)
            , rrdset_last_slot(st)
            , rrdset_last_entry_t(st)
            , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
            , st->update_every
            , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?1:0
            , st->usec_since_last_update
            , st->collected_total
            , st->last_collected_total
    );

    unsigned long memory = st->memsize;

    RRDDIM *rd;
    rrddim_foreach_read(rd, st) {

        memory += rd->memsize;

        buffer_sprintf(wb,
                "\t\t\t\t{\n"
                "\t\t\t\t\t\"id\": \"%s\",\n"
                "\t\t\t\t\t\"name\": \"%s\",\n"
                "\t\t\t\t\t\"entries\": %ld,\n"
                "\t\t\t\t\t\"isHidden\": %d,\n"
                "\t\t\t\t\t\"algorithm\": \"%s\",\n"
                "\t\t\t\t\t\"multiplier\": " COLLECTED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"divisor\": " COLLECTED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"last_entry_t\": %ld,\n"
                "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
                "\t\t\t\t\t\"memory\": %lu\n"
                "\t\t\t\t}%s\n"
                , rd->id
                , rd->name
                , rd->entries
                , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0
                , rrd_algorithm_name(rd->algorithm)
                , rd->multiplier
                , rd->divisor
                , rd->last_collected_time.tv_sec
                , rd->collected_value
                , rd->calculated_value
                , rd->last_collected_value
                , rd->last_calculated_value
                , rd->memsize
                , rd->next?",":""
        );
    }
예제 #26
0
파일: web_api_v1.c 프로젝트: cppmx/netdata
int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
    int ret = 400;
    buffer_flush(w->response.data);

    BUFFER *dimensions = NULL;

    const char *chart = NULL
    , *before_str = NULL
    , *after_str = NULL
    , *points_str = NULL
    , *multiply_str = NULL
    , *divide_str = NULL
    , *label = NULL
    , *units = NULL
    , *label_color = NULL
    , *value_color = NULL
    , *refresh_str = NULL
    , *precision_str = NULL
    , *scale_str = NULL
    , *alarm = NULL;

    int group = GROUP_AVERAGE;
    uint32_t options = 0x00000000;

    while(url) {
        char *value = mystrsep(&url, "/?&");
        if(!value || !*value) continue;

        char *name = mystrsep(&value, "=");
        if(!name || !*name) continue;
        if(!value || !*value) continue;

        debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);

        // name and value are now the parameters
        // they are not null and not empty

        if(!strcmp(name, "chart")) chart = value;
        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
            if(!dimensions)
                dimensions = buffer_create(100);

            buffer_strcat(dimensions, "|");
            buffer_strcat(dimensions, value);
        }
        else if(!strcmp(name, "after")) after_str = value;
        else if(!strcmp(name, "before")) before_str = value;
        else if(!strcmp(name, "points")) points_str = value;
        else if(!strcmp(name, "group")) {
            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
        }
        else if(!strcmp(name, "options")) {
            options |= web_client_api_request_v1_data_options(value);
        }
        else if(!strcmp(name, "label")) label = value;
        else if(!strcmp(name, "units")) units = value;
        else if(!strcmp(name, "label_color")) label_color = value;
        else if(!strcmp(name, "value_color")) value_color = value;
        else if(!strcmp(name, "multiply")) multiply_str = value;
        else if(!strcmp(name, "divide")) divide_str = value;
        else if(!strcmp(name, "refresh")) refresh_str = value;
        else if(!strcmp(name, "precision")) precision_str = value;
        else if(!strcmp(name, "scale")) scale_str = value;
        else if(!strcmp(name, "alarm")) alarm = value;
    }

    if(!chart || !*chart) {
        buffer_no_cacheable(w->response.data);
        buffer_sprintf(w->response.data, "No chart id is given at the request.");
        goto cleanup;
    }

    int scale = (scale_str && *scale_str)?str2i(scale_str):100;

    RRDSET *st = rrdset_find(host, chart);
    if(!st) st = rrdset_find_byname(host, chart);
    if(!st) {
        buffer_no_cacheable(w->response.data);
        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1, scale, 0);
        ret = 200;
        goto cleanup;
    }
    st->last_accessed_time = now_realtime_sec();

    RRDCALC *rc = NULL;
    if(alarm) {
        rc = rrdcalc_find(st, alarm);
        if (!rc) {
            buffer_no_cacheable(w->response.data);
            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0);
            ret = 200;
            goto cleanup;
        }
    }

    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;

    if(!multiply) multiply = 1;
    if(!divide) divide = 1;

    int refresh = 0;
    if(refresh_str && *refresh_str) {
        if(!strcmp(refresh_str, "auto")) {
            if(rc) refresh = rc->update_every;
            else if(options & RRDR_OPTION_NOT_ALIGNED)
                refresh = st->update_every;
            else {
                refresh = (int)(before - after);
                if(refresh < 0) refresh = -refresh;
            }
        }
        else {
            refresh = str2i(refresh_str);
            if(refresh < 0) refresh = -refresh;
        }
    }

    if(!label) {
        if(alarm) {
            char *s = (char *)alarm;
            while(*s) {
                if(*s == '_') *s = ' ';
                s++;
            }
            label = alarm;
        }
        else if(dimensions) {
            const char *dim = buffer_tostring(dimensions);
            if(*dim == '|') dim++;
            label = dim;
        }
        else
            label = st->name;
    }
    if(!units) {
        if(alarm) {
            if(rc->units)
                units = rc->units;
            else
                units = "";
        }
        else if(options & RRDR_OPTION_PERCENTAGE)
            units = "%";
        else
            units = st->units;
    }

    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
          , w->id
          , chart
          , alarm?alarm:""
          , (dimensions)?buffer_tostring(dimensions):""
          , after
          , before
          , points
          , group
          , options
    );

    if(rc) {
        if (refresh > 0) {
            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
            w->response.data->expires = now_realtime_sec() + refresh;
        }
        else buffer_no_cacheable(w->response.data);

        if(!value_color) {
            switch(rc->status) {
                case RRDCALC_STATUS_CRITICAL:
                    value_color = "red";
                    break;

                case RRDCALC_STATUS_WARNING:
                    value_color = "orange";
                    break;

                case RRDCALC_STATUS_CLEAR:
                    value_color = "brightgreen";
                    break;

                case RRDCALC_STATUS_UNDEFINED:
                    value_color = "lightgrey";
                    break;

                case RRDCALC_STATUS_UNINITIALIZED:
                    value_color = "#000";
                    break;

                default:
                    value_color = "grey";
                    break;
            }
        }

        buffer_svg(w->response.data,
                label,
                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
                units,
                label_color,
                value_color,
                precision,
                scale,
                options
        );
        ret = 200;
    }
    else {
        time_t latest_timestamp = 0;
        int value_is_null = 1;
        calculated_number n = NAN;
        ret = 500;

        // if the collected value is too old, don't calculate its value
        if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
            ret = rrdset2value_api_v1(st, w->response.data, &n, (dimensions) ? buffer_tostring(dimensions) : NULL
                                      , points, after, before, group, 0, options, NULL, &latest_timestamp, &value_is_null);

        // if the value cannot be calculated, show empty badge
        if (ret != 200) {
            buffer_no_cacheable(w->response.data);
            value_is_null = 1;
            n = 0;
            ret = 200;
        }
        else if (refresh > 0) {
            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
            w->response.data->expires = now_realtime_sec() + refresh;
        }
        else buffer_no_cacheable(w->response.data);

        // render the badge
        buffer_svg(w->response.data,
                label,
                (value_is_null)?NAN:(n * multiply / divide),
                units,
                label_color,
                value_color,
                precision,
                scale,
                options
        );
    }

    cleanup:
    buffer_free(dimensions);
    return ret;
}
예제 #27
0
파일: registry.c 프로젝트: cppmx/netdata
static inline void registry_json_header(RRDHOST *host, struct web_client *w, const char *action, const char *status) {
    buffer_flush(w->response.data);
    w->response.data->contenttype = CT_APPLICATION_JSON;
    buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
            action, status, host->registry_hostname, host->machine_guid);
}