aboutsummaryrefslogtreecommitdiff
path: root/src/ping903q.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-03-07 09:48:38 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-03-07 12:13:39 +0200
commitf6769c969ee779c43ab0df18f7388b9d5a307f96 (patch)
tree8d976ee0f482d33528898ebe89723f6cb02adec4 /src/ping903q.c
parent417659b757afc85fc437f3da462c09a4b86c1282 (diff)
downloadping903-f6769c969ee779c43ab0df18f7388b9d5a307f96.tar.gz
ping903-f6769c969ee779c43ab0df18f7388b9d5a307f96.tar.bz2
Simplify entry point structure.
The /ip entry point is gone. The /hosts entry point accepts new query arguments: (1) select=LIST, which introduces a list of IP addresses (hostnames) to query and (2) attr=LIST - a list of attributes to return in each stat object. In both cases, LIST is a comma-separated list of values. The "select" argument can be used together with explicit host (/hosts/IP?select=LIST), which is equivalent to /hosts/?select=IP,LIST. The /match entry point takes the "select" query argument as well. It return array of match objects. Each match object contains at least the following attributes: - name: original host name used in the request - hosts: array of monitored host names or IPs corresponding to that name, (can be empty). If an error occurred (e.g. host name cannot be resolved) the "error" attribute contains the textual description of the error. The ping903q utility now accepts one or more IP addresses as arguments in all modes, except nagios check. The statistics or matches for each IP are returned separately. * README: Document changes. * doc/ping903q.1: Likewise. * src/ping903.c (http_log): Log query arguments, if any. (ept_host_stat): Accept query arguments: select and attr. Return array of stat objects. (ept_ip_stat): Remove. (ept_ip_match): Accept query arguments: select. Rewrite output. (endpoint): Remove /ip * src/ping903.h (get_ipaddr_stat): Remove. (get_host_matches): Change signature. * src/ping903q.c (HTTP_BUF_INITIALIZER): New macro. (http_buf_init, http_buf_free): New functions. (query_host,match_host): Removed. (query_hosts,match_hosts): Removed. (query_host_nagios): New function. * src/pinger.c (get_ipaddr_stat): Remove. (hostping_match): New function. (get_host_matches): Rewrite.
Diffstat (limited to 'src/ping903q.c')
-rw-r--r--src/ping903q.c416
1 files changed, 332 insertions, 84 deletions
diff --git a/src/ping903q.c b/src/ping903q.c
index 64c17bb..8b51367 100644
--- a/src/ping903q.c
+++ b/src/ping903q.c
@@ -186,6 +186,8 @@ struct http_buf {
size_t len;
};
+#define HTTP_BUF_INITIALIZER { NULL, 0, 0 }
+
static char const *
http_resp_get_header(struct http_resp *resp, char const *name)
{
@@ -206,6 +208,29 @@ http_resp_get_header(struct http_resp *resp, char const *name)
}
static void
+http_buf_init(struct http_buf *hbuf)
+{
+ memset(hbuf, 0, sizeof(*hbuf));
+}
+
+static void
+http_buf_free(struct http_buf *hbuf)
+{
+ free(hbuf->base);
+ http_buf_init(hbuf);
+}
+
+static void
+http_buf_add(struct http_buf *hbuf, char const *str, size_t len)
+{
+ while (hbuf->len + len >= hbuf->size) {
+ hbuf->base = e2nrealloc(hbuf->base, &hbuf->size, 1);
+ }
+ memcpy(hbuf->base + hbuf->len, str, len);
+ hbuf->len += len;
+}
+
+static void
http_readline(struct http_buf *hbuf)
{
hbuf->len = 0;
@@ -314,10 +339,9 @@ static void
http_recv(struct http_resp *resp)
{
enum input_state { is_initial, is_headers, is_content } state = is_initial;
- struct http_buf hbuf;
+ struct http_buf hbuf = HTTP_BUF_INITIALIZER;
char const *hval;
- memset(&hbuf, 0, sizeof(hbuf));
http_resp_reset(resp);
while (state != is_content) {
http_readline(&hbuf);
@@ -353,6 +377,7 @@ http_recv(struct http_resp *resp)
}
}
}
+ http_buf_free(&hbuf);
hval = http_resp_get_header(resp, "content-length");
if (hval) {
@@ -711,7 +736,7 @@ ejson_get(struct json_value *obj, char *name, int type)
}
static int
-print_host_status(struct json_value *obj, void *unused)
+print_host_status(struct json_value *obj)
{
struct json_value *jv;
int alive;
@@ -755,102 +780,238 @@ print_host_status(struct json_value *obj, void *unused)
return alive ? EX_NAGIOS_OK : EX_NAGIOS_CRITICAL;
}
-static int
-unknown_host_status(char const *host, char const *msg)
+static char *
+argvjoin(int argc, char **argv)
{
- abend("%s: %s", host, msg);
- return EX_NAGIOS_UNKNOWN;
+ int i;
+ size_t len = argc - 1;
+ char *ret, *p, *q;
+
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]);
+
+ ret = emalloc(len + 1);
+ p = ret;
+ for (i = 0; i < argc; i++) {
+ q = argv[i];
+ while ((*p++ = *q++))
+ ;
+ p[-1] = ',';
+ }
+ p[-1] = 0;
+ return ret;
}
+enum {
+ ARG_SELECT,
+ ARG_ATTR,
+ NARG_MAX
+};
+
+static char const *arg_str[] = {
+ "select=",
+ "attr=",
+};
static char *
-resolve_host(char const *name)
+urlformat(char const *baseurl, char const *param, char **argv)
{
- struct addrinfo hints, *res;
- char hbuf[NI_MAXHOST];
- int rc;
+ size_t len;
+ char *buf;
+ int i;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
- hints.ai_protocol = IPPROTO_TCP;
- rc = getaddrinfo(name, NULL, &hints, &res);
- if (rc)
- abend("%s: %s", name, gai_strerror(rc));
- if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
- NULL, 0, NI_NUMERICHOST)) {
- /* shouldn't happen */
- abend("%s: can't resolve hostname", name);
+ len = strlen(baseurl);
+ if (param)
+ len += strlen(param) + 1;
+ if (argv) {
+ for (i = 0; i < NARG_MAX; i++) {
+ if (argv[i])
+ len += strlen(arg_str[i]) + strlen(argv[i]) + 1;
+ }
}
- freeaddrinfo(res);
- return estrdup(hbuf);
+
+ buf = emalloc(len + 1);
+ strcpy(buf, baseurl);
+ if (param) {
+ strcat(buf, "/");
+ strcat(buf, param);
+ }
+
+ if (argv) {
+ char const *delim[] = { "?", "&" };
+ for (i = 0; i < NARG_MAX; i++) {
+ if (argv[i]) {
+ strcat(buf, delim[!!i]);
+ strcat(buf, arg_str[i]);
+ strcat(buf, argv[i]);
+ }
+ }
+ }
+
+ return buf;
}
static void
-query_host(char const *name,
- int (*report)(struct json_value *, void *),
- int (*unknown)(char const*, char const*),
- void *report_data)
+query_hosts(int argc, char **argv)
{
int rc;
struct http_resp resp;
struct json_value *obj;
- char url[1024];
+ char *url;
+ char *reqargv[NARG_MAX];
char const *hval;
char *p;
- char *ipstr = resolve_host(name);
- ssize_t n;
-
+ size_t i;
+ size_t count[] = { 0, 0 };
+
+ memset(reqargv, 0, sizeof(reqargv));
+ reqargv[ARG_SELECT] = argvjoin(argc, argv);
+
+ http_resp_init(&resp);
if (resolve_ip) {
- n = snprintf(url, sizeof(url), "/ip/%s", ipstr);
- } else {
- n = snprintf(url, sizeof(url), "/host/%s", name);
+ size_t len;
+ struct json_value *ar;
+ struct http_buf hbuf = HTTP_BUF_INITIALIZER;
+
+ url = urlformat("/match", NULL, reqargv);
+ free(reqargv[ARG_SELECT]);
+
+ http_query("GET", url, std_headers, &resp);
+ if (resp.code != 200) {
+ abend("%s", resp.reason);
+ }
+
+ hval = http_resp_get_header(&resp, "content-type");
+ if (!hval || strcmp(hval, "application/json")) {
+ abend("missing or unsupported content type");
+ }
+
+ rc = json_parse_string(resp.content, &ar, &p);
+ if (rc != JSON_E_NOERR)
+ abend("%s near %s", json_strerror(rc), p);
+ if (ar->type != json_array)
+ abend("returned entity has wrong type");
+
+ len = json_array_length(ar);
+
+ for (i = 0; i < len; i++) {
+ struct json_value *obj, *hosts, *err;
+ char const *name;
+
+ if (json_array_get(ar, i, &obj))
+ abend("can't get element %zu", i);
+
+ name = ejson_get(obj, "name", json_string)->v.s;
+
+ if (json_object_get(obj, "error", &err) == 0) {
+ error("%s: %s", name, err->v.s);
+ count[0]++;
+ } else if (json_object_get(obj, "hosts", &hosts) == 0
+ && hosts->type == json_array) {
+ size_t hlen, j;
+
+ hlen = json_array_length(hosts);
+ if (hlen == 0) {
+ error("%s: host is not monitored",
+ name);
+ count[0]++;
+ } else {
+ for (j = 0; j < hlen; j++) {
+ struct json_value *jv;
+ if (!json_array_get(hosts, j, &jv)
+ && jv->type == json_string) {
+ if (j)
+ http_buf_add(&hbuf, ",", 1);
+ http_buf_add(&hbuf, jv->v.s, strlen(jv->v.s));
+ }
+ }
+ }
+ } else
+ abend("malformed element %zu", i);
+ }
+
+ json_value_free(ar);
+
+ if (hbuf.len == 0)
+ exit(EX_NAGIOS_CRITICAL);
+ http_buf_add(&hbuf, "", 1);
+ reqargv[ARG_SELECT] = hbuf.base ;
+ http_resp_reset(&resp);
}
- if (n < 0 || n == sizeof(url)) {
- abend("bad host name or IP");
- }
- http_resp_init(&resp);
+ url = urlformat("/host", NULL, reqargv);
+ free(reqargv[ARG_SELECT]);
+
http_query("GET", url, std_headers, &resp);
+ free(url);
switch (resp.code) {
case 200:
break;
case 404:
- exit(unknown(name, "host is not monitored"));
+ abend("%s: host is not monitored", argv[0]);
default:
- exit(unknown(name, resp.reason));
+ if (argc == 1)
+ abend("%s: %s", argv[0], resp.reason);
+ else
+ abend("%s", resp.reason);
}
hval = http_resp_get_header(&resp, "content-type");
if (!hval || strcmp(hval, "application/json")) {
- exit(unknown(name, "missing or unsupported content type"));
+ abend("missing or unsupported content type");
}
rc = json_parse_string(resp.content, &obj, &p);
if (rc != JSON_E_NOERR)
abend("%s near %s", json_strerror(rc), p);
- if (obj->type == json_null)
- exit(unknown(name, "host is not monitored"));
- if (obj->type != json_object)
- exit(unknown(name, "returned entity has wrong type"));
- exit(report(obj, report_data));
+
+ if (obj->type != json_array)
+ abend("returned entity has wrong type");
+
+ for (i = 0; i < json_array_length(obj); i++) {
+ struct json_value *jv;
+
+ if (json_array_get(obj, i, &jv))
+ abend("can't get value %d: %s", i, strerror(errno));
+
+ switch (jv->type) {
+ case json_object:
+ count[print_host_status(jv) != EX_NAGIOS_OK]++;
+ break;
+
+ case json_null:
+ error("%s: host is not monitored", argv[i]);
+ count[0]++;
+ break;
+ default:
+ abend("returned entity is malformed");
+ }
+ }
+ if (count[1] == 0)
+ exit(EX_NAGIOS_OK);
+ if (count[0] == 0)
+ exit(EX_NAGIOS_CRITICAL);
+ exit(EX_NAGIOS_WARNING);
}
static void
-match_host(char const *name)
+match_hosts(int argc, char **argv)
{
int rc;
struct http_resp resp;
- struct json_value *obj;
- char url[1024];
+ struct json_value *ar;
+ char *url;
char const *hval;
char *p;
- ssize_t n;
size_t i, len;
+ char *reqargv[NARG_MAX];
+ int count[2] = { 0, 0 };
+
+ memset(reqargv, 0, sizeof(reqargv));
+ reqargv[ARG_SELECT] = argvjoin(argc, argv);
+ url = urlformat("/match", NULL, reqargv);
+ free(reqargv[ARG_SELECT]);
- n = snprintf(url, sizeof(url), "/match/%s", name);
- if (n == -1 || n == sizeof(url)) {
- abend("url buffer overflow");
- }
http_resp_init(&resp);
http_query("GET", url, std_headers, &resp);
if (resp.code != 200) {
@@ -862,29 +1023,59 @@ match_host(char const *name)
abend("missing or unsupported content type");
}
- rc = json_parse_string(resp.content, &obj, &p);
+ rc = json_parse_string(resp.content, &ar, &p);
if (rc != JSON_E_NOERR)
abend("%s near %s", json_strerror(rc), p);
- if (obj->type != json_array)
+ if (ar->type != json_array)
abend("returned entity has wrong type");
- len = json_array_length(obj);
- if (json_array_length(obj) == 0) {
- if (verbose)
- error("no matching hosts found");
- exit(EX_NAGIOS_CRITICAL);
- }
-
+ len = json_array_length(ar);
for (i = 0; i < len; i++) {
- struct json_value *jv;
- if (json_array_get(obj, i, &jv)) {
- abend("can't get element %lu", (unsigned long) i);
- }
- if (jv->type != json_string)
- abend("bad type of element %lu", (unsigned long) i);
- printf("%s\n", jv->v.s);
+ char const *name;
+ struct json_value *obj, *hosts, *err;
+ if (json_array_get(ar, i, &obj))
+ abend("can't get element %zu", i);
+ if (obj->type != json_object)
+ abend("bad type of element %zu", i);
+ name = ejson_get(obj, "name", json_string)->v.s;
+ if (json_object_get(obj, "error", &err) == 0) {
+ error("%s: %s", name, err->v.s);
+ count[1]++;
+ } else if (json_object_get(obj, "hosts", &hosts) == 0) {
+ size_t hlen;
+
+ if (hosts->type != json_array)
+ abend("malformed element %zu", i);
+ hlen = json_array_length(hosts);
+ if (hlen == 0) {
+ error("%s: no matches", name);
+ count[1]++;
+ } else {
+ size_t j;
+
+ printf("%s", name);
+ for (j = 0; j < hlen; j++) {
+ struct json_value *jv;
+ if (json_array_get(hosts, j, &jv))
+ abend("can't get host "
+ "element %zu", j);
+ else if (jv->type != json_string)
+ abend("bad type of host "
+ "element %zu", j);
+ else
+ printf(" %s", jv->v.s);
+ }
+ putchar('\n');
+ count[0]++;
+ }
+ } else
+ abend("malformed element %zu", i);
}
- exit(EX_NAGIOS_OK);
+ if (count[1] == 0)
+ exit(EX_NAGIOS_OK);
+ if (count[0] == 0)
+ exit(EX_NAGIOS_CRITICAL);
+ exit(EX_NAGIOS_WARNING);
}
static void
@@ -924,7 +1115,7 @@ query_all(void)
abend("can't get element %lu", (unsigned long) i);
}
if (jv->type == json_object)
- count[print_host_status(jv, NULL) != EX_NAGIOS_OK]++;
+ count[print_host_status(jv) != EX_NAGIOS_OK]++;
}
if (count[1] == 0)
exit(EX_NAGIOS_OK);
@@ -1017,9 +1208,8 @@ nagios_format_output(int status, double loss, double rta,
}
static int
-nagios_check(struct json_value *obj, void *data)
+nagios_check(struct json_value *obj, struct nagios_check_data *cd)
{
- struct nagios_check_data *cd = data;
struct json_value *jv;
char const *name;
int status;
@@ -1068,11 +1258,73 @@ nagios_unknown(char const *name, char const *msg)
return EX_NAGIOS_UNKNOWN;
}
+static void
+query_host_nagios(char const *host, struct nagios_check_data *chkdata)
+{
+ int rc;
+ struct http_resp resp;
+ struct json_value *obj, *jv;
+ char *url;
+ char const *hval;
+ char *p;
+
+ url = urlformat("/host", host, NULL);
+ http_resp_init(&resp);
+ http_query("GET", url, std_headers, &resp);
+ free(url);
+ switch (resp.code) {
+ case 200:
+ break;
+ case 404:
+ exit(nagios_unknown(host, "host is not monitored"));
+ default:
+ exit(nagios_unknown(host, resp.reason));
+ }
+
+ hval = http_resp_get_header(&resp, "content-type");
+ if (!hval || strcmp(hval, "application/json")) {
+ exit(nagios_unknown(host,
+ "missing or unsupported content type"));
+ }
+
+ rc = json_parse_string(resp.content, &obj, &p);
+ if (rc != JSON_E_NOERR)
+ abend("%s near %s", json_strerror(rc), p);
+
+ switch (obj->type) {
+ case json_null:
+ exit(nagios_unknown(host, "host is not monitored"));
+
+ case json_array:
+ break;
+
+ default:
+ exit(nagios_unknown(host, "returned entity has wrong type"));
+ }
+
+ if (json_array_get(obj, 0, &jv)) {
+ if (errno != ENOENT)
+ abend("can't get array element: %s", strerror(errno));
+ exit(nagios_unknown(host, "empty array returned"));
+ }
+
+ switch (jv->type) {
+ case json_object:
+ rc = nagios_check(jv, chkdata);
+ break;
+ case json_null:
+ rc = nagios_unknown(host, "host is not monitored");
+ break;
+ default:
+ rc = nagios_unknown(host, "returned entity is malformed");
+ }
+ exit(rc);
+}
void
usage(void)
{
- printf("Usage: %s [-hrVv] [-f FILE] [HOST]\n", progname);
+ printf("Usage: %s [-hrVv] [-f FILE] [HOST...]\n", progname);
printf(" or: %s [-N] [-p PREFIX] -H HOST -c RTA,PCT%% -w RTA,PCT%%\n",
progname);
printf(" or: %s -m HOST\n", progname);
@@ -1215,8 +1467,8 @@ main(int argc, char **argv)
parse_nagios_threshold(w_opt, &chkdata.wth);
break;
case MODE_MATCH:
- if (argc != 1)
- abend("-m requires exactly one command line argument");
+ if (argc == 0)
+ abend("-m requires one or more command line arguments");
break;
}
@@ -1243,21 +1495,17 @@ main(int argc, char **argv)
switch (argc) {
case 0:
query_all();
- case 1:
- query_host(argv[0],
- print_host_status,
- unknown_host_status, NULL);
default:
- abend("bad number of arguments");
+ query_hosts(argc, argv);
}
break;
case MODE_NAGIOS_CHECK:
- query_host(host, nagios_check, nagios_unknown, &chkdata);
+ query_host_nagios(host, &chkdata);
break;
case MODE_MATCH:
- match_host(argv[0]);
+ match_hosts(argc, argv);
}
exit(EX_NAGIOS_UNKNOWN);
}

Return to:

Send suggestions and report system problems to the System administrator.