diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-12 12:16:52 +0100 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-12 12:41:48 +0100 |
commit | 46ac092358df2a779754788f5f32bcb8aae5e2a0 (patch) | |
tree | 31aa768cf766c43d19fd20f179d46797e7ef2640 | |
parent | f9991b5a09024a7b7bd3ee52338f0cda5ddde794 (diff) | |
download | ping903-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.ac | 3 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 14 | ||||
-rw-r--r-- | src/config.c | 1 | ||||
-rw-r--r-- | src/defs.h | 24 | ||||
-rw-r--r-- | src/json.h | 12 | ||||
-rw-r--r-- | src/main.c | 36 | ||||
-rw-r--r-- | src/mem.c | 36 | ||||
-rw-r--r-- | src/ping903.c | 13 | ||||
-rw-r--r-- | src/ping903.conf | 3 | ||||
-rw-r--r-- | src/ping903.h | 16 | ||||
-rw-r--r-- | src/ping903q.c | 579 | ||||
-rw-r--r-- | src/pinger.c | 26 |
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); @@ -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, @@ -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) |