From b5e0751f5972502b0824883357e4424b026a8bdf Mon Sep 17 00:00:00 2001 From: Jef Poskanzer Date: Thu, 7 Feb 2013 12:35:17 -0800 Subject: [PATCH] (Mostly) switched error reporting to a single routine that handles JSON mode. --- src/iperf_api.c | 86 ++---------------------- src/iperf_api.h | 4 +- src/iperf_error.c | 146 +++++++++++++++++++++++++---------------- src/iperf_server_api.c | 4 +- src/iperf_udp.c | 2 +- src/iperf_util.c | 74 +++++++++++++++++++++ src/iperf_util.h | 4 ++ src/main.c | 22 +++---- 8 files changed, 184 insertions(+), 158 deletions(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index 616ff41..de5f51b 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -1387,15 +1386,15 @@ iperf_print_intermediate(struct iperf_test *test) /* sum up all streams */ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); if (irp == NULL) { - fprintf(stderr, "iperf_print_intermediate Error: interval_results is NULL\n"); + iperf_err(test, "iperf_print_intermediate error: interval_results is NULL"); return; } bytes += irp->bytes_transferred; if (test->protocol->id == Ptcp && has_tcpinfo_retransmits()) retransmits += irp->this_retrans; } - if (bytes <=0 ) { /* this can happen if timer goes off just when client exits */ - fprintf(stderr, "error: bytes <= 0!\n"); + if (bytes <=0) { /* this can happen if timer goes off just when client exits */ + iperf_err(test, "error: bytes <= 0!"); return; } /* next build string with sum of all streams */ @@ -1613,7 +1612,7 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON * irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* get last entry in linked list */ if (irp == NULL) { - fprintf(stderr, "print_interval_results Error: interval_results is NULL \n"); + iperf_err(test, "print_interval_results error: interval_results is NULL"); return; } if (!test->json_output) { @@ -1826,80 +1825,3 @@ iperf_json_finish(struct iperf_test *test) test->json_top = test->json_start = test->json_intervals = test->json_end = NULL; return 0; } - -/* Helper routine for building cJSON objects in a printf-like manner. -** -** Sample call: -** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s); -** -** The four formatting characters and the types they expect are: -** %b boolean int -** %d integer int64_t -** %f floating point double -** %s string char * -** If the values you're passing in are not these exact types, you must -** cast them, there is no automatic type coercion/widening here. -** -** The colons mark the end of field names, and blanks are ignored. -** -** This routine is not particularly robust, but it's not part of the API, -** it's just for internal iperf3 use. -** -** It currently lives in iperf_api.c, rather than iperf_util.c, because -** putting it in iperf_util.c would mean adding -lm to more executables, -** because the cJSON package uses libm/floor(). If I get around to fixing -** that, then this routine can move. -*/ -cJSON* -iperf_json_printf(const char *format, ...) -{ - cJSON* o; - va_list argp; - const char *cp; - char name[100]; - char* np; - cJSON* j; - - o = cJSON_CreateObject(); - if (o == NULL) - return NULL; - va_start(argp, format); - np = name; - for (cp = format; *cp != '\0'; ++cp) { - switch (*cp) { - case ' ': - break; - case ':': - *np = '\0'; - break; - case '%': - ++cp; - switch (*cp) { - case 'b': - j = cJSON_CreateBool(va_arg(argp, int)); - break; - case 'd': - j = cJSON_CreateInt(va_arg(argp, int64_t)); - break; - case 'f': - j = cJSON_CreateFloat(va_arg(argp, double)); - break; - case 's': - j = cJSON_CreateString(va_arg(argp, char *)); - break; - default: - return NULL; - } - if (j == NULL) - return NULL; - cJSON_AddItemToObject(o, name, j); - np = name; - break; - default: - *np++ = *cp; - break; - } - } - va_end(argp); - return o; -} diff --git a/src/iperf_api.h b/src/iperf_api.h index 08a7af8..c0cb0cc 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -195,10 +195,10 @@ void iperf_test_reset(struct iperf_test *); /* JSON output routines. */ int iperf_json_start(struct iperf_test *); int iperf_json_finish(struct iperf_test *); -cJSON* iperf_json_printf(const char *format, ...); /* Error routines. */ -void iperf_error(char *); +void iperf_err(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3))); +void iperf_errexit(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3),noreturn)); char *iperf_strerror(int); extern int i_errno; enum { diff --git a/src/iperf_error.c b/src/iperf_error.c index c17158f..d640661 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -11,9 +11,47 @@ #include #include #include +#include +#include #include "iperf.h" #include "iperf_api.h" +static void +iperf_verr(struct iperf_test *test, const char *format, va_list argp) +{ + char str[1000]; + + vsnprintf(str, sizeof(str), format, argp); + if (test != NULL && test->json_output && test->json_top != NULL) { + cJSON_AddStringToObject(test->json_top, "error", str); + iperf_json_finish(test); + } else + fprintf(stderr, "iperf3: %s\n", str); +} + +/* Do a printf to stderr. */ +void +iperf_err(struct iperf_test *test, const char *format, ...) +{ + va_list argp; + + va_start(argp, format); + iperf_verr(test, format, argp); + va_end(argp); +} + +/* Do a printf to stderr, then exit. */ +void +iperf_errexit(struct iperf_test *test, const char *format, ...) +{ + va_list argp; + + va_start(argp, format); + iperf_verr(test, format, argp); + va_end(argp); + exit(1); +} + int i_errno; char * @@ -28,198 +66,198 @@ iperf_strerror(int i_errno) switch (i_errno) { case IENONE: - snprintf(errstr, len, "No error"); + snprintf(errstr, len, "no error"); break; case IESERVCLIENT: - snprintf(errstr, len, "Iperf cannot be both server and client"); + snprintf(errstr, len, "cannot be both server and client"); break; case IENOROLE: - snprintf(errstr, len, "Iperf instance must either be a client (-c) or server (-s)"); + snprintf(errstr, len, "must either be a client (-c) or server (-s)"); break; case IECLIENTONLY: - snprintf(errstr, len, "Some option you are trying to set is client only"); + snprintf(errstr, len, "some option you are trying to set is client only"); break; case IEDURATION: - snprintf(errstr, len, "Test duration too long (maximum = %d seconds)", MAX_TIME); + snprintf(errstr, len, "test duration too long (maximum = %d seconds)", MAX_TIME); break; case IENUMSTREAMS: - snprintf(errstr, len, "Number of parallel streams too large (maximum = %d)", MAX_STREAMS); + snprintf(errstr, len, "number of parallel streams too large (maximum = %d)", MAX_STREAMS); break; case IEBLOCKSIZE: - snprintf(errstr, len, "Block size too large (maximum = %d bytes)", MAX_BLOCKSIZE); + snprintf(errstr, len, "block size too large (maximum = %d bytes)", MAX_BLOCKSIZE); break; case IEBUFSIZE: - snprintf(errstr, len, "Socket buffer size too large (maximum = %d bytes)", MAX_TCP_BUFFER); + snprintf(errstr, len, "socket buffer size too large (maximum = %d bytes)", MAX_TCP_BUFFER); break; case IEINTERVAL: - snprintf(errstr, len, "Report interval too large (maximum = %d seconds)", MAX_INTERVAL); + snprintf(errstr, len, "report interval too large (maximum = %d seconds)", MAX_INTERVAL); break; case IEMSS: snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS); break; case IENEWTEST: - snprintf(errstr, len, "Unable to create a new test"); + snprintf(errstr, len, "unable to create a new test"); perr = 1; break; case IEINITTEST: - snprintf(errstr, len, "Test initialization failed"); + snprintf(errstr, len, "test initialization failed"); perr = 1; break; case IELISTEN: - snprintf(errstr, len, "Unable to start listener for connections"); + snprintf(errstr, len, "unable to start listener for connections"); perr = 1; break; case IECONNECT: - snprintf(errstr, len, "Unable to connect to server"); + snprintf(errstr, len, "unable to connect to server"); herr = 1; perr = 1; break; case IEACCEPT: - snprintf(errstr, len, "Unable to accept connection from client"); + snprintf(errstr, len, "unable to accept connection from client"); herr = 1; perr = 1; break; case IESENDCOOKIE: - snprintf(errstr, len, "Unable to send cookie to server"); + snprintf(errstr, len, "unable to send cookie to server"); perr = 1; break; case IERECVCOOKIE: - snprintf(errstr, len, "Unable to receive cookie to server"); + snprintf(errstr, len, "unable to receive cookie to server"); perr = 1; break; case IECTRLWRITE: - snprintf(errstr, len, "Unable to write to the control socket"); + snprintf(errstr, len, "unable to write to the control socket"); perr = 1; break; case IECTRLREAD: - snprintf(errstr, len, "Unable to read from the control socket"); + snprintf(errstr, len, "unable to read from the control socket"); perr = 1; break; case IECTRLCLOSE: - snprintf(errstr, len, "Control socket has closed unexpectedly"); + snprintf(errstr, len, "control socket has closed unexpectedly"); break; case IEMESSAGE: - snprintf(errstr, len, "Received an unknown control message"); + snprintf(errstr, len, "received an unknown control message"); break; case IESENDMESSAGE: - snprintf(errstr, len, "Unable to send control message"); + snprintf(errstr, len, "unable to send control message"); perr = 1; break; case IERECVMESSAGE: - snprintf(errstr, len, "Unable to receive control message"); + snprintf(errstr, len, "unable to receive control message"); perr = 1; break; case IESENDPARAMS: - snprintf(errstr, len, "Unable to send parameters to server"); + snprintf(errstr, len, "unable to send parameters to server"); perr = 1; break; case IERECVPARAMS: - snprintf(errstr, len, "Unable to receive parameters from client"); + snprintf(errstr, len, "unable to receive parameters from client"); perr = 1; break; case IEPACKAGERESULTS: - snprintf(errstr, len, "Unable to package results"); + snprintf(errstr, len, "unable to package results"); perr = 1; break; case IESENDRESULTS: - snprintf(errstr, len, "Unable to send results"); + snprintf(errstr, len, "unable to send results"); perr = 1; break; case IERECVRESULTS: - snprintf(errstr, len, "Unable to receive results"); + snprintf(errstr, len, "unable to receive results"); perr = 1; break; case IESELECT: - snprintf(errstr, len, "Select failed"); + snprintf(errstr, len, "select failed"); perr = 1; break; case IECLIENTTERM: - snprintf(errstr, len, "The client has terminated"); + snprintf(errstr, len, "the client has terminated"); break; case IESERVERTERM: - snprintf(errstr, len, "The server has terminated"); + snprintf(errstr, len, "the server has terminated"); break; case IEACCESSDENIED: - snprintf(errstr, len, "The server is busy running a test. try again later"); + snprintf(errstr, len, "the server is busy running a test. try again later"); break; case IESETNODELAY: - snprintf(errstr, len, "Unable to set TCP NODELAY"); + snprintf(errstr, len, "unable to set TCP NODELAY"); perr = 1; break; case IESETMSS: - snprintf(errstr, len, "Unable to set TCP MSS"); + snprintf(errstr, len, "unable to set TCP MSS"); perr = 1; break; case IESETBUF: - snprintf(errstr, len, "Unable to set socket buffer size"); + snprintf(errstr, len, "unable to set socket buffer size"); perr = 1; break; case IESETTOS: - snprintf(errstr, len, "Unable to set IP TOS"); + snprintf(errstr, len, "unable to set IP TOS"); perr = 1; break; case IESETCOS: - snprintf(errstr, len, "Unable to set IPv6 traffic class"); + snprintf(errstr, len, "unable to set IPv6 traffic class"); perr = 1; break; case IEREUSEADDR: - snprintf(errstr, len, "Unable to reuse address on socket"); + snprintf(errstr, len, "unable to reuse address on socket"); perr = 1; break; case IENONBLOCKING: - snprintf(errstr, len, "Unable to set socket to non-blocking"); + snprintf(errstr, len, "unable to set socket to non-blocking"); perr = 1; break; case IESETWINDOWSIZE: - snprintf(errstr, len, "Unable to set socket window size"); + snprintf(errstr, len, "unable to set socket window size"); perr = 1; break; case IEPROTOCOL: - snprintf(errstr, len, "Protocol does not exist"); + snprintf(errstr, len, "protocol does not exist"); break; case IECREATESTREAM: - snprintf(errstr, len, "Unable to create a new stream"); + snprintf(errstr, len, "unable to create a new stream"); herr = 1; perr = 1; break; case IEINITSTREAM: - snprintf(errstr, len, "Unable to initialize stream"); + snprintf(errstr, len, "unable to initialize stream"); herr = 1; perr = 1; break; case IESTREAMLISTEN: - snprintf(errstr, len, "Unable to start stream listener"); + snprintf(errstr, len, "unable to start stream listener"); perr = 1; break; case IESTREAMCONNECT: - snprintf(errstr, len, "Unable to connect stream"); + snprintf(errstr, len, "unable to connect stream"); herr = 1; perr = 1; break; case IESTREAMACCEPT: - snprintf(errstr, len, "Unable to accept stream connection"); + snprintf(errstr, len, "unable to accept stream connection"); perr = 1; break; case IESTREAMWRITE: - snprintf(errstr, len, "Unable to write to stream socket"); + snprintf(errstr, len, "unable to write to stream socket"); perr = 1; break; case IESTREAMREAD: - snprintf(errstr, len, "Unable to read from stream socket"); + snprintf(errstr, len, "unable to read from stream socket"); perr = 1; break; case IESTREAMCLOSE: - snprintf(errstr, len, "Stream socket has closed unexpectedly"); + snprintf(errstr, len, "stream socket has closed unexpectedly"); break; case IESTREAMID: - snprintf(errstr, len, "Stream has an invalid id"); + snprintf(errstr, len, "stream has an invalid id"); break; case IENEWTIMER: - snprintf(errstr, len, "Unable to create new timer"); + snprintf(errstr, len, "unable to create new timer"); perr = 1; break; case IEUPDATETIMER: - snprintf(errstr, len, "Unable to update timer"); + snprintf(errstr, len, "unable to update timer"); perr = 1; break; } @@ -234,9 +272,3 @@ iperf_strerror(int i_errno) return errstr; } - -void -iperf_error(char *estr) -{ - fprintf(stderr, "%s: %s\n", estr, iperf_strerror(i_errno)); -} diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index dd1fc99..f74ea5d 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -159,7 +159,7 @@ iperf_handle_message_server(struct iperf_test *test) // XXX: Need to rethink how this behaves to fit API if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) { if (rval == 0) { - fprintf(stderr, "The client has unexpectedly closed the connection.\n"); + iperf_err(test, "the client has unexpectedly closed the connection"); i_errno = IECTRLCLOSE; test->state = IPERF_DONE; return 0; @@ -202,7 +202,7 @@ iperf_handle_message_server(struct iperf_test *test) i_errno = IECLIENTTERM; // XXX: Remove this line below! - fprintf(stderr, "The client has terminated.\n"); + iperf_err(test, "the client has terminated"); SLIST_FOREACH(sp, &test->streams, streams) { FD_CLR(sp->socket, &test->read_set); FD_CLR(sp->socket, &test->write_set); diff --git a/src/iperf_udp.c b/src/iperf_udp.c index e2e041d..538b790 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -67,7 +67,7 @@ iperf_udp_recv(struct iperf_stream *sp) sp->packet_count = pcount; } else { sp->outoforder_packets++; - fprintf(stderr, "OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d\n", pcount, sp->packet_count, sp->socket); + iperf_err(sp->test, "OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d", pcount, sp->packet_count, sp->socket); } /* jitter measurement */ diff --git a/src/iperf_util.c b/src/iperf_util.c index 1795d03..3fccd1b 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include "config.h" +#include "cjson.h" /* make_cookie * @@ -177,3 +179,75 @@ cpu_util(double *pcpu) *pcpu = ((ctemp - clast) / timediff) * 100; } + +/* Helper routine for building cJSON objects in a printf-like manner. +** +** Sample call: +** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s); +** +** The four formatting characters and the types they expect are: +** %b boolean int +** %d integer int64_t +** %f floating point double +** %s string char * +** If the values you're passing in are not these exact types, you must +** cast them, there is no automatic type coercion/widening here. +** +** The colons mark the end of field names, and blanks are ignored. +** +** This routine is not particularly robust, but it's not part of the API, +** it's just for internal iperf3 use. +*/ +cJSON* +iperf_json_printf(const char *format, ...) +{ + cJSON* o; + va_list argp; + const char *cp; + char name[100]; + char* np; + cJSON* j; + + o = cJSON_CreateObject(); + if (o == NULL) + return NULL; + va_start(argp, format); + np = name; + for (cp = format; *cp != '\0'; ++cp) { + switch (*cp) { + case ' ': + break; + case ':': + *np = '\0'; + break; + case '%': + ++cp; + switch (*cp) { + case 'b': + j = cJSON_CreateBool(va_arg(argp, int)); + break; + case 'd': + j = cJSON_CreateInt(va_arg(argp, int64_t)); + break; + case 'f': + j = cJSON_CreateFloat(va_arg(argp, double)); + break; + case 's': + j = cJSON_CreateString(va_arg(argp, char *)); + break; + default: + return NULL; + } + if (j == NULL) + return NULL; + cJSON_AddItemToObject(o, name, j); + np = name; + break; + default: + *np++ = *cp; + break; + } + } + va_end(argp); + return o; +} diff --git a/src/iperf_util.h b/src/iperf_util.h index 5afb6ba..7369190 100644 --- a/src/iperf_util.h +++ b/src/iperf_util.h @@ -10,6 +10,8 @@ #ifndef __IPERF_UTIL_H #define __IPERF_UTIL_H +#include "cjson.h" + void make_cookie(char *); int is_closed(int); @@ -24,4 +26,6 @@ int delay(int64_t ns); void cpu_util(double *); +cJSON* iperf_json_printf(const char *format, ...); + #endif diff --git a/src/main.c b/src/main.c index 827e96a..b9b3058 100644 --- a/src/main.c +++ b/src/main.c @@ -74,10 +74,8 @@ main(int argc, char **argv) #endif test = iperf_new_test(); - if (!test) { - iperf_error("create new test error"); - exit(1); - } + if (!test) + iperf_errexit(NULL, "create new test error - %s", iperf_strerror(i_errno)); iperf_defaults(test); /* sets defaults */ // XXX: Check signal for errors? @@ -94,16 +92,14 @@ main(int argc, char **argv) } if (iperf_parse_arguments(test, argc, argv) < 0) { - iperf_error("parameter error"); + iperf_err(test, "parameter error - %s", iperf_strerror(i_errno)); fprintf(stderr, "\n"); usage_long(); exit(1); } - if (iperf_run(test) < 0) { - iperf_error("error"); - exit(1); - } + if (iperf_run(test) < 0) + iperf_errexit(test, "error - %s", iperf_strerror(i_errno)); iperf_free_test(test); @@ -144,17 +140,15 @@ iperf_run(struct iperf_test * test) case 's': for (;;) { if (iperf_run_server(test) < 0) { - iperf_error("error"); + iperf_err(test, "error - %s", iperf_strerror(i_errno)); fprintf(stderr, "\n"); } iperf_reset_test(test); } break; case 'c': - if (iperf_run_client(test) < 0) { - iperf_error("error"); - exit(1); - } + if (iperf_run_client(test) < 0) + iperf_errexit(test, "error - %s", iperf_strerror(i_errno)); break; default: usage();