aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2020-02-12 12:16:52 +0100
committerSergey Poznyakoff <gray@gnu.org.ua>2020-02-12 12:41:48 +0100
commit46ac092358df2a779754788f5f32bcb8aae5e2a0 (patch)
tree31aa768cf766c43d19fd20f179d46797e7ef2640
parentf9991b5a09024a7b7bd3ee52338f0cda5ddde794 (diff)
downloadping903-46ac092358df2a779754788f5f32bcb8aae5e2a0.tar.gz
ping903-46ac092358df2a779754788f5f32bcb8aae5e2a0.tar.bz2
Implement simple query utility
* configure.ac: Test for ranlib * src/.gitignore: Update. * src/ping903q.c: New file. * src/Makefile.am: Build ping903q * src/defs.h: New file. * src/mem.h: New file. * src/json.h (json_2nrealloc): New proto. * src/config.c: New statement: "access-log-verbose" Change error codes to positive values. * src/main.c: Use DEFAULT_CONFIG_FILE instead of hardcoding the file name. * src/ping903.c: Log json object only if access-log-verbose has been requested. * src/ping903.conf: Document access-log-verbose * src/ping903.h (HOSTADDR): Rename tv to xmit_tv. New member: start_tv. * src/pinger.c: Keep the time when first packet has been sent in the start_tv member.
-rw-r--r--configure.ac3
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am14
-rw-r--r--src/config.c1
-rw-r--r--src/defs.h24
-rw-r--r--src/json.h12
-rw-r--r--src/main.c36
-rw-r--r--src/mem.c36
-rw-r--r--src/ping903.c13
-rw-r--r--src/ping903.conf3
-rw-r--r--src/ping903.h16
-rw-r--r--src/ping903q.c579
-rw-r--r--src/pinger.c26
13 files changed, 689 insertions, 75 deletions
diff --git a/configure.ac b/configure.ac
index f82e099..fae3176 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,7 +23,8 @@ AM_INIT_AUTOMAKE([-Wall -Werror 1.11.5 foreign tar-ustar silent-rules])
# Checks for programs.
AC_PROG_CC
-
+AC_PROG_RANLIB
+
# Checks for libraries.
AC_CHECK_LIB([microhttpd],[MHD_start_daemon],[],
[AC_MSG_ERROR([required library microhttpd not found
diff --git a/src/.gitignore b/src/.gitignore
index deb4593..2112ee5 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1 +1,2 @@
/ping903
+/ping903q
diff --git a/src/Makefile.am b/src/Makefile.am
index 981856f..cc6f1ff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,16 +17,20 @@
sbin_PROGRAMS=ping903
ping903_SOURCES=\
main.c\
- logger.c\
ping903.c\
ping903.h\
pinger.c\
config.c\
- remoteip.c\
- json.c\
- json.h
+ remoteip.c
if COND_TCPD
ping903_SOURCES += wrapacl.c
endif
-AM_YFLAGS = -dtv
+
+bin_PROGRAMS = ping903q
+ping903q_SOURCES = ping903q.c
+
+LDADD = ./libping903.a
+
+noinst_LIBRARIES = libping903.a
+libping903_a_SOURCES = json.c json.h mem.c logger.c defs.h
diff --git a/src/config.c b/src/config.c
index 5c72a28..779dacd 100644
--- a/src/config.c
+++ b/src/config.c
@@ -143,6 +143,7 @@ struct cf_stmt statements[] = {
{ "data-length", STMT_T_ULONG, &data_length },
{ "syslog-facility", STMT_T_CALLBACK, NULL, cf_syslog_facility },
{ "access-log", STMT_T_BOOL, &httpd_access_log },
+ { "access-log-verbose", STMT_T_BOOL, &httpd_log_verbose },
{ NULL }
};
diff --git a/src/defs.h b/src/defs.h
new file mode 100644
index 0000000..d6d824e
--- /dev/null
+++ b/src/defs.h
@@ -0,0 +1,24 @@
+#include <stdarg.h>
+
+#ifndef DEFAULT_ADDRESS
+# define DEFAULT_ADDRESS "0.0.0.0"
+#endif
+#ifndef DEFAULT_SERVICE
+# define DEFAULT_SERVICE "8080"
+#endif
+#ifndef DEFAULT_CONFIG_FILE
+# define DEFAULT_CONFIG_FILE "/etc/ping903.conf"
+#endif
+
+void emalloc_die(void);
+void *emalloc(size_t s);
+char *estrdup(char const *s);
+void *e2nrealloc(void *p, size_t *pn, size_t s);
+
+void vlogger(int prio, char const *fmt, va_list ap);
+void fatal(char const *fmt, ...);
+void error(char const *fmt, ...);
+void info(char const *fmt, ...);
+void syslog_enable(void);
+void set_progname(char const *arg);
+int set_log_facility(char const *arg);
diff --git a/src/json.h b/src/json.h
index 8f98368..8e4a169 100644
--- a/src/json.h
+++ b/src/json.h
@@ -51,6 +51,8 @@ struct json_format
int json_value_format(struct json_value *obj, struct json_format *fmt,
size_t level);
void json_value_free(struct json_value *);
+
+void *json_2nrealloc(void *p, size_t *pn, size_t s);
struct json_value *json_new_null(void);
struct json_value *json_new_bool(int b);
@@ -75,11 +77,11 @@ int json_array_get(struct json_value *j, size_t idx,
enum {
- JSON_E_NOERR = 0,
- JSON_E_NOMEM = -1,
- JSON_E_BADTOK = -2,
- JSON_E_BADDELIM = -3,
- JSON_E_BADSTRING = -4
+ JSON_E_NOERR,
+ JSON_E_NOMEM,
+ JSON_E_BADTOK,
+ JSON_E_BADDELIM,
+ JSON_E_BADSTRING
};
int json_parse_string(char const *input, struct json_value **retval,
diff --git a/src/main.c b/src/main.c
index 30b2fbd..b78dec0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,42 +25,10 @@
#include <pthread.h>
#include "ping903.h"
#include "json.h"
+#include "defs.h"
-static char *config_file = "/etc/ping903.conf";
+static char *config_file = DEFAULT_CONFIG_FILE;
int verbose;
-
-
-void
-emalloc_die(void)
-{
- fatal("not enough memory");
- exit(2);
-}
-
-void *
-emalloc(size_t s)
-{
- void *p = malloc(s);
- if (!p)
- emalloc_die();
- return p;
-}
-
-void *
-e2nrealloc(void *p, size_t *pn, size_t s)
-{
- extern void *json_2nrealloc(void *p, size_t *pn, size_t s);
- char *n = json_2nrealloc(p, pn, s);
- if (!n)
- emalloc_die();
- return n;
-}
-
-char *
-estrdup(char const *s)
-{
- return strcpy(emalloc(strlen(s) + 1), s);
-}
enum state {
RUNNING, /* Program is running */
diff --git a/src/mem.c b/src/mem.c
new file mode 100644
index 0000000..4312308
--- /dev/null
+++ b/src/mem.c
@@ -0,0 +1,36 @@
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include "defs.h"
+#include "json.h"
+
+void
+emalloc_die(void)
+{
+ fatal("not enough memory");
+ exit(2);
+}
+
+void *
+emalloc(size_t s)
+{
+ void *p = malloc(s);
+ if (!p)
+ emalloc_die();
+ return p;
+}
+
+void *
+e2nrealloc(void *p, size_t *pn, size_t s)
+{
+ char *n = json_2nrealloc(p, pn, s);
+ if (!n)
+ emalloc_die();
+ return n;
+}
+
+char *
+estrdup(char const *s)
+{
+ return strcpy(emalloc(strlen(s) + 1), s);
+}
diff --git a/src/ping903.c b/src/ping903.c
index c3c0a46..cdf0752 100644
--- a/src/ping903.c
+++ b/src/ping903.c
@@ -28,16 +28,11 @@
#include <netdb.h>
#include "ping903.h"
#include "json.h"
-
-#ifndef DEFAULT_ADDRESS
-# define DEFAULT_ADDRESS "0.0.0.0"
-#endif
-#ifndef DEFAULT_SERVICE
-# define DEFAULT_SERVICE "8080"
-#endif
+#include "defs.h"
char *httpd_addr;
int httpd_access_log = 0;
+int httpd_log_verbose = 0;
static int
open_node(char const *node, char const *serv, struct sockaddr **saddr)
@@ -147,7 +142,8 @@ http_log(struct MHD_Connection *connection,
if (!httpd_access_log)
return;
-
+ if (!httpd_log_verbose)
+ str = NULL;
ipstr = get_remote_ip(connection);
host = MHD_lookup_connection_value(connection,
@@ -196,7 +192,6 @@ struct strbuf {
static void
strbuf_writer(void *closure, char const *text, size_t len)
{
- extern void *json_2nrealloc(void *p, size_t *pn, size_t s);
struct strbuf *sb = closure;
if (sb->err)
return;
diff --git a/src/ping903.conf b/src/ping903.conf
index 864ed85..d314946 100644
--- a/src/ping903.conf
+++ b/src/ping903.conf
@@ -48,6 +48,9 @@
# 0, f, nil, false, no, off - disable logging.
#access-log off
+# Show JSON replies along with access logs.
+#access-log-verbose off
+
# Register trusted IP (or network) for the purpose of HTTP logging. For
# requests coming from that IP the value of the X-Forwarded-For header will
# be trusted. Default is empty. Argument is IP[/MASK], where IP stands for
diff --git a/src/ping903.h b/src/ping903.h
index ea72810..fbcc471 100644
--- a/src/ping903.h
+++ b/src/ping903.h
@@ -20,13 +20,6 @@
void ping903(void);
-void vlogger(int prio, char const *fmt, va_list ap);
-void fatal(char const *fmt, ...);
-void error(char const *fmt, ...);
-void info(char const *fmt, ...);
-void syslog_enable(void);
-void set_progname(char const *arg);
-int set_log_facility(char const *arg);
/* Configuration file parser */
@@ -66,11 +59,6 @@ int readconfig(char const *file);
int cf_trusted_ip(int mode, union cf_callback_arg *arg, void *data);
int cf_syslog_facility(int mode, union cf_callback_arg *arg, void *data);
-void emalloc_die(void);
-void *emalloc(size_t s);
-char *estrdup(char const *s);
-void *e2nrealloc(void *p, size_t *pn, size_t s);
-
char *get_remote_ip(struct MHD_Connection *conn);
struct host_stat {
@@ -96,7 +84,8 @@ typedef struct hostaddr {
struct hostaddr *next;
/* Current ping statistics */
- struct timeval tv;
+ struct timeval start_tv;
+ struct timeval xmit_tv;
unsigned long xmit_count;
unsigned long recv_count;
double tmin; /* minimum round trip time */
@@ -113,6 +102,7 @@ extern int verbose;
extern int fatal_signals[];
extern char *httpd_addr;
extern int httpd_access_log;
+extern int httpd_log_verbose;
extern char *pidfile;
extern unsigned long ping_interval;
extern unsigned long ping_count;
diff --git a/src/ping903q.c b/src/ping903q.c
new file mode 100644
index 0000000..a6ce27f
--- /dev/null
+++ b/src/ping903q.c
@@ -0,0 +1,579 @@
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <sys/types.h> /* See NOTES */
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "json.h"
+#include "defs.h"
+
+static char *config_file = DEFAULT_CONFIG_FILE;
+FILE *http;
+int verbose;
+char const http_version[] = "HTTP/1.1";
+
+enum {
+ EX_P903_ALIVE = 0,
+ EX_P903_NOTALIVE = 1,
+ EX_P903_MIXED = 2
+};
+
+static char *
+read_listen(char **ret_service)
+{
+ FILE *fp;
+ int ln = 0;
+ char buf[1024];
+ int skip_to_eol = 0;
+ char *retval = NULL;
+
+ *ret_service = NULL;
+
+ fp = fopen(config_file, "r");
+ if (!fp) {
+ if (errno == ENOENT)
+ return NULL;
+ else {
+ fatal("can't open %s: %s",
+ config_file, strerror(errno));
+ exit(EX_CONFIG);
+ }
+ }
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ int len;
+ char *p;
+
+ ln++;
+ len = strlen(buf);
+ if (len == 0)
+ continue;
+
+ if (skip_to_eol) {
+ skip_to_eol = buf[len-1] != '\n';
+ continue;
+ }
+
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ else if (!feof(fp)) {
+ error("%s:%d: line too long", config_file, ln);
+ skip_to_eol = 1;
+ continue;
+ }
+
+ while (len > 0 && isspace(buf[len-1]))
+ buf[--len] = 0;
+
+ if (len == 0)
+ continue;
+
+ for (p = buf; (*p == ' ' || *p == '\t'); p++)
+ ;
+ if (*p == 0 || *p == '#')
+ continue;
+
+ if (strncmp(p, "listen", 6) == 0) {
+ char *q;
+
+ p += 6;
+ while (*p && (*p == ' '||*p == '\t'))
+ p++;
+ retval = estrdup(p);
+ q = strchr(retval, ':');
+ if (q) {
+ *q++ = 0;
+ if (*q) {
+ *ret_service = estrdup(q);
+ }
+ }
+ break;
+ }
+ }
+
+ fclose(fp);
+ return retval;
+}
+
+static void
+http_connect(char *node, char *service)
+{
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int fd;
+ int rc;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ rc = getaddrinfo(node, service, &hints, &res);
+ if (rc) {
+ fatal("%s: %s", node, gai_strerror(rc));
+ exit(EX_UNAVAILABLE);
+ }
+
+ fd = socket(res->ai_family, res->ai_socktype, 0);
+ if (fd == -1) {
+ fatal("socket: %s", strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ if (connect(fd, (struct sockaddr *)res->ai_addr, res->ai_addrlen)) {
+ fatal("failed to connect to %s:%s: %s",
+ node, service, strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ http = fdopen(fd, "w+");
+ if (http == NULL) {
+ fatal("failed to open socket: %s", strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+ freeaddrinfo(res);
+}
+
+struct http_resp {
+ int code; /* HTTP response code */
+ char *reason; /* Human-readable status */
+ char **hv; /* Headers */
+ size_t hc; /* Number of headers in hv */
+ size_t hn; /* Max capacity of hv */
+ char *content;
+ size_t content_length;
+};
+
+struct http_buf {
+ char *base;
+ size_t size;
+ size_t len;
+};
+
+static char const *
+http_resp_get_header(struct http_resp *resp, char const *name)
+{
+ int i;
+ size_t len = strlen(name);
+
+ for (i = 0; i < resp->hc; i++) {
+ if (strlen(resp->hv[i]) > len
+ && resp->hv[i][len] == ':'
+ && strncasecmp(resp->hv[i], name, len) == 0) {
+ char const *ret = resp->hv[i] + len + 1;
+ while (*ret && (*ret == ' ' || *ret == '\t'))
+ ret++;
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+static void
+http_readline(struct http_buf *hbuf)
+{
+ hbuf->len = 0;
+ while (1) {
+ if (hbuf->len == hbuf->size) {
+ hbuf->base = e2nrealloc(hbuf->base, &hbuf->size, 1);
+ }
+ if (fgets(hbuf->base + hbuf->len, hbuf->size, http) == NULL) {
+ if (feof(http)) {
+ fatal("connection closed prematurely");
+ exit(EX_PROTOCOL);
+ } else {
+ fatal("error reading from socket: %s",
+ strerror(errno));
+ exit(EX_OSERR);
+ }
+ }
+ hbuf->len += strlen(hbuf->base + hbuf->len);
+ if (hbuf->base[hbuf->len-1] == '\n') {
+ hbuf->base[--hbuf->len] = 0;
+ if (hbuf->base[hbuf->len-1] == '\r')
+ hbuf->base[--hbuf->len] = 0;
+ break;
+ }
+ }
+}
+
+static int
+split3(const char *input, char *res[3], int flag)
+{
+ size_t len;
+ char *p;
+
+ p = strchr(input, ' ');
+ if (!p)
+ return 1;
+
+ len = p - input;
+ res[0] = malloc(len + 1);
+ if (!res[0])
+ return -1;
+ memcpy(res[0], input, len);
+ res[0][len] = 0;
+
+ input = p + 1;
+
+ p = (flag ? strrchr : strchr)(input, ' ');
+
+ if (!p)
+ return 1;
+ len = p - input;
+ res[1] = malloc(len + 1);
+ if (!res[1])
+ return -1;
+ memcpy(res[1], input, len);
+ res[1][len] = 0;
+
+ res[2] = strdup(p + 1);
+ if (!res[2])
+ return -1;
+
+ return 0;
+}
+
+int
+strsplit3(const char *input, char *result[3], int flag)
+{
+ char *tmp[3] = { NULL, NULL, NULL };
+ int rc = split3(input, tmp, flag);
+ if (rc) {
+ int ec = errno;
+ free(tmp[0]);
+ free(tmp[1]);
+ free(tmp[2]);
+ errno = ec;
+ } else {
+ result[0] = tmp[0];
+ result[1] = tmp[1];
+ result[2] = tmp[2];
+ }
+ return rc;
+}
+
+static void
+http_resp_init(struct http_resp *resp)
+{
+ memset(resp, 0, sizeof(*resp));
+}
+
+static void
+http_resp_reset(struct http_resp *resp)
+{
+ size_t i;
+
+ resp->code = 0;
+ free(resp->reason);
+ resp->reason = NULL;
+ for (i = 0; i < resp->hc; i++) {
+ free(resp->hv[i]);
+ resp->hv[i] = NULL;
+ }
+ resp->hc = 0;
+ free(resp->content);
+ resp->content = NULL;
+ resp->content_length = 0;
+}
+
+static void
+http_recv(struct http_resp *resp)
+{
+ enum input_state { is_initial, is_headers, is_content } state = is_initial;
+ struct http_buf hbuf;
+ char const *hval;
+
+ memset(&hbuf, 0, sizeof(hbuf));
+ http_resp_reset(resp);
+ while (state != is_content) {
+ http_readline(&hbuf);
+ if (state == is_initial) {
+ char *res[3];
+
+ if (strsplit3(hbuf.base, res, 0)) {
+ fatal("malformed HTTP response");
+ exit(EX_PROTOCOL);
+ }
+ if (strcmp(res[0], http_version)) {
+ fatal("unsupported HTTP version: %s", res[0]);
+ exit(EX_PROTOCOL);
+ }
+ free(res[0]);
+
+ resp->code = atoi(res[1]);
+ if (resp->code <= 0 || resp->code > 559) {
+ fatal("bad response code: %s", res[1]);
+ exit(EX_PROTOCOL);
+ }
+ free(res[1]);
+ resp->reason = res[2];
+ state = is_headers;
+ } else if (state == is_headers) {
+ if (resp->hc == resp->hn) {
+ resp->hv = json_2nrealloc(resp->hv,
+ &resp->hn,
+ sizeof(resp->hv[0]));
+ }
+ if (hbuf.len > 0) {
+ resp->hv[resp->hc++] = estrdup(hbuf.base);
+ } else {
+ resp->hv[resp->hc] = NULL;
+ state = is_content;
+ }
+ }
+ }
+
+ hval = http_resp_get_header(resp, "content-length");
+ if (hval) {
+ char *p;
+ unsigned long len = strtoul(hval, &p, 10);
+ if (*p) {
+ fatal("malformed content-length");
+ exit(EX_PROTOCOL);
+ }
+ resp->content = emalloc(len + 1);
+ resp->content_length = len;
+
+ p = resp->content;
+ while (len) {
+ ssize_t n = fread(p, 1, len, http);
+ if (n == -1) {
+ fatal("socket read error: %s",
+ strerror(errno));
+ exit(EX_OSERR);
+ }
+ if (n == 0) {
+ error("warning: short read from socket");
+ exit(EX_OSERR);
+ }
+ len -= n;
+ p += n;
+ }
+ *p = 0;
+ }
+}
+
+static void
+http_query(char const *meth, char const *url, char **hdr)
+{
+ size_t i;
+ int host = 0;
+
+ fprintf(http, "%s %s HTTP/1.1\r\n", meth, url);
+ if (hdr) {
+ for (i = 0; hdr[i]; i++) {
+ fprintf(http, "%s\r\n", hdr[i]);
+ if (!host && strncasecmp(hdr[i], "host:", 5) == 0)
+ host = 1;
+ }
+ }
+ if (!host)
+ fprintf(http, "Host: localhost\r\n");
+ fprintf(http, "\r\n");
+}
+
+static char const *
+json_strerror(int ec)
+{
+ static char *json_errstr[] = {
+ [JSON_E_NOERR] = "No error",
+ [JSON_E_NOMEM] = "Not enough memory",
+ [JSON_E_BADTOK] = "Unrecognized token",
+ [JSON_E_BADDELIM] = "Bad delimiter",
+ [JSON_E_BADSTRING] = "Malformed string"
+ };
+ if (ec < 0 || ec >= sizeof(json_errstr) / sizeof(json_errstr[0]))
+ return "Unknown error";
+ return json_errstr[ec];
+}
+
+static struct json_value *
+ejson_get(struct json_value *obj, char *name, int type)
+{
+ struct json_value *jv;
+ if (json_object_get(obj, name, &jv)) {
+ fatal("no \"%s\" member in the response", name);
+ exit(EX_DATAERR);
+ }
+ if (jv->type != type) {
+ fatal("\"%s\" member has wrong type", name);
+ exit(EX_DATAERR);
+ }
+ return jv;
+}
+
+static int
+print_host_status(struct json_value *obj)
+{
+ struct json_value *jv;
+ int alive;
+ char const *name;
+
+ jv = ejson_get(obj, "name", json_string);
+ name = jv->v.s;
+
+ jv = ejson_get(obj, "status", json_bool);
+ if (!jv->v.b) {
+ printf("no response from %s\n", name);
+ return EX_P903_NOTALIVE;
+ }
+
+ alive = ejson_get(obj, "alive", json_bool)->v.b;
+ printf("%s %s\n", name, alive ? "alive" : "not alive");
+ if (verbose) {
+ double xmit = ejson_get(obj, "xmit", json_number)->v.n;
+ double recv = ejson_get(obj, "recv", json_number)->v.n;
+ double start_ts = ejson_get(obj, "start-timestamp",
+ json_number)->v.n;
+ double recv_ts = ejson_get(obj, "recv-timestamp",
+ json_number)->v.n;
+
+ printf("--- %s ping statistics ---\n", name);
+ printf("%lu packets transmitted, %lu received, %d%% packet loss, time %.0fms\n",
+ (unsigned long) xmit, (unsigned long) recv,
+ (int)(100 * (xmit - recv) / recv),
+ (recv_ts - start_ts) * 1000);
+ printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n",
+ ejson_get(obj, "tmin", json_number)->v.n,
+ ejson_get(obj, "avg", json_number)->v.n,
+ ejson_get(obj, "tmax", json_number)->v.n,
+ ejson_get(obj, "stddev", json_number)->v.n);
+ }
+ return alive ? EX_P903_ALIVE : EX_P903_NOTALIVE;
+}
+
+static void
+query_host(char const *name)
+{
+ int rc;
+ struct http_resp resp;
+ struct json_value *obj;
+ char url[1024];
+ char const *hval;
+ char *p;
+ ssize_t n = snprintf(url, sizeof(url), "/host/%s", name);
+ if (n < 0 || n == sizeof(url)) {
+ fatal("bad host name or IP");
+ exit(EX_DATAERR);
+ }
+ http_query("GET", url, NULL);
+ http_resp_init(&resp);
+ http_recv(&resp);
+ if (resp.code != 200) {
+ fatal("%s", resp.reason);
+ exit(EX_UNAVAILABLE);
+ }
+
+ hval = http_resp_get_header(&resp, "content-type");
+ if (!hval || strcmp(hval, "application/json")) {
+ fatal("missing or unsupported content type");
+ exit(EX_UNAVAILABLE);
+ }
+
+ rc = json_parse_string(resp.content, &obj, &p);
+ if (rc != JSON_E_NOERR) {
+ fatal("%s near %s", json_strerror(rc), p);
+ exit(EX_DATAERR);
+ }
+ if (obj->type != json_object) {
+ fatal("returned entity has wrong type");
+ exit(EX_DATAERR);
+ }
+ exit(print_host_status(obj));
+}
+
+static void
+query_all(void)
+{
+ int rc;
+ struct http_resp resp;
+ struct json_value *obj;
+ char const *hval;
+ char *p;
+ size_t count[2] = { 0, 0 };
+ size_t len;
+ size_t i;
+
+ http_query("GET", "/host", NULL);
+ http_resp_init(&resp);
+ http_recv(&resp);
+ if (resp.code != 200) {
+ fatal("%s", resp.reason);
+ exit(EX_UNAVAILABLE);
+ }
+
+ hval = http_resp_get_header(&resp, "content-type");
+ if (!hval || strcmp(hval, "application/json")) {
+ fatal("missing or unsupported content type");
+ exit(EX_UNAVAILABLE);
+ }
+
+ rc = json_parse_string(resp.content, &obj, &p);
+ if (rc != JSON_E_NOERR) {
+ fatal("%s near %s", json_strerror(rc), p);
+ exit(EX_DATAERR);
+ }
+ if (obj->type != json_array) {
+ fatal("returned entity has wrong type");
+ exit(EX_DATAERR);
+ }
+ len = json_array_length(obj);
+ for (i = 0; i < len; i++) {
+ struct json_value *jv;
+ if (json_array_get(obj, i, &jv)) {
+ fatal("can't get element %lu", (unsigned long) i);
+ exit(EX_SOFTWARE);
+ }
+ if (jv->type == json_object)
+ count[print_host_status(jv)]++;
+ }
+ if (count[1] == 0)
+ exit(EX_P903_ALIVE);
+ if (count[0] == 0)
+ exit(EX_P903_NOTALIVE);
+ exit(EX_P903_MIXED);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *p, *node, *service;
+
+ set_progname(argv[0]);
+ while ((c = getopt(argc, argv, "c:v")) != EOF) {
+ switch (c) {
+ case 'c':
+ config_file = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ exit(EX_USAGE);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ p = node = read_listen(&service);
+ if (!node || !node[0])
+ node = "127.0.0.1";
+ if (!service)
+ service = DEFAULT_SERVICE;
+ http_connect(node, service);
+ free(p);
+
+ switch (argc) {
+ case 0:
+ query_all();
+ case 1:
+ query_host(argv[0]);
+ default:
+ fatal("bad number of arguments");
+ }
+ exit(EX_USAGE);
+}
diff --git a/src/pinger.c b/src/pinger.c
index 145ab19..b8d65ee 100644
--- a/src/pinger.c
+++ b/src/pinger.c
@@ -247,8 +247,8 @@ send_echo(HOSTADDR *host, unsigned char *ping_buffer)
ssize_t n;
int seqno;
- gettimeofday(&host->tv, NULL);
- memcpy(icmp->icmp_data, &host->tv, sizeof(host->tv));
+ gettimeofday(&host->xmit_tv, NULL);
+ memcpy(icmp->icmp_data, &host->xmit_tv, sizeof(host->xmit_tv));
if (data_buffer)
memcpy(icmp->icmp_data + PING_HEADER_LEN, data_buffer,
data_length > PING_HEADER_LEN
@@ -257,7 +257,7 @@ send_echo(HOSTADDR *host, unsigned char *ping_buffer)
buflen = ICMP_HEADER_LEN + data_length;
- seqno = seqno_alloc(host, &host->tv);
+ seqno = seqno_alloc(host, &host->xmit_tv);
if (seqno == -1) {
sendq_enqueue_unlocked(host);
return;
@@ -270,6 +270,8 @@ send_echo(HOSTADDR *host, unsigned char *ping_buffer)
if (n < 0)
error("%s: sendto: %s", host->name, strerror(errno));
else {
+ if (host->xmit_count == 0)
+ host->start_tv = host->xmit_tv;
host->xmit_count++;
if (n != buflen)
error("ping: wrote %s %d chars, ret=%d\n",
@@ -346,6 +348,12 @@ host_reset(HOSTADDR *host)
host->tsumsq = 0;
}
+static inline double
+timeval_to_double(struct timeval const *tv)
+{
+ return (double)tv->tv_sec + (double)tv->tv_usec / 1000000;
+}
+
int
get_host_stat(HOSTADDR *host, struct json_value **retval)
{
@@ -362,10 +370,14 @@ get_host_stat(HOSTADDR *host, struct json_value **retval)
if (!(v = json_new_bool(host_stat_is_valid(st)))
|| json_object_set(obj, "status", v))
goto err;
- ts = (double)host->tv.tv_sec + (double)host->tv.tv_usec / 1000000;
+ ts = timeval_to_double(&host->xmit_tv);
if (!(v = json_new_number(ts))
|| json_object_set(obj, "xmit-timestamp", v))
goto err;
+ ts = timeval_to_double(&host->start_tv);
+ if (!(v = json_new_number(ts))
+ || json_object_set(obj, "start-timestamp", v))
+ goto err;
if (host_stat_is_valid(st)) {
int is_alive = st->xmit_count - st->recv_count
@@ -382,8 +394,7 @@ get_host_stat(HOSTADDR *host, struct json_value **retval)
"stddev"
}
*/
- ts = (double)st->tv.tv_sec
- + (double)st->tv.tv_usec / 1000000;
+ ts = timeval_to_double(&st->tv);
if (!(v = json_new_number(ts))
|| json_object_set(obj, "recv-timestamp", v))
goto err;
@@ -551,8 +562,7 @@ p903_receiver(void *p)
memcpy(&tv_orig, tp, sizeof (tv_orig));
timersub(&tv_now, &tv_orig, &tv_diff);
- rtt = ((double) tv_diff.tv_sec) * 1000.0 +
- ((double) tv_diff.tv_usec) / 1000.0;
+ rtt = timeval_to_double(&tv_diff) * 1000.0;
host->tsum += rtt;
host->tsumsq += rtt * rtt;
if (rtt < host->tmin)

Return to:

Send suggestions and report system problems to the System administrator.