diff --git a/src/iperf.h b/src/iperf.h index 188f06a..22037df 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -174,6 +174,9 @@ struct iperf_test char *congestion; /* -C option */ char *pidfile; /* -P option */ + char *logfile; /* --logfile option */ + FILE *outfile; + int ctrl_sck; int listener; int prot_listener; diff --git a/src/iperf3.1 b/src/iperf3.1 index d2d13ba..588ef3b 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -55,6 +55,9 @@ give more detailed output .BR -J ", " --json " " output in JSON format .TP +.BR --logfile " \fIfile\fR" +send output to a log file. +.TP .BR -d ", " --debug " " emit debugging output. Primarily (perhaps exclusively) of use to developers. diff --git a/src/iperf_api.c b/src/iperf_api.c index 05de7b7..744e74c 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -562,6 +562,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"sctp", no_argument, NULL, OPT_SCTP}, #endif {"pidfile", required_argument, NULL, 'I'}, + {"logfile", required_argument, NULL, OPT_LOGFILE}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} @@ -791,6 +792,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->pidfile = strdup(optarg); server_flag = 1; break; + case OPT_LOGFILE: + test->logfile = strdup(optarg); + break; case 'h': default: usage_long(); @@ -798,6 +802,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } } + /* Set logging to a file if specified, otherwise stdout*/ + if (test->logfile) { + test->outfile = fopen(test->logfile, "a+"); + if (test->outfile == NULL) { + i_errno = IELOGFILE; + return -1; + } + } + else { + test->outfile = stdout; + } + /* Check flag / role compatibility. */ if (test->role == 'c' && server_flag) { i_errno = IESERVERONLY; @@ -2524,9 +2540,8 @@ iperf_json_finish(struct iperf_test *test) str = cJSON_Print(test->json_top); if (str == NULL) return -1; - fputs(str, stdout); - putchar('\n'); - fflush(stdout); + fprintf(test->outfile, "%s\n", str); + iflush(test); free(str); cJSON_Delete(test->json_top); test->json_top = test->json_start = test->json_intervals = test->json_end = NULL; @@ -2608,9 +2623,15 @@ iprintf(struct iperf_test *test, const char* format, ...) int r; if (test->title) - printf("%s: ", test->title); + fprintf(test->outfile, "%s: ", test->title); va_start(argp, format); - r = vprintf(format, argp); + r = vfprintf(test->outfile, format, argp); va_end(argp); return r; } + +int +iflush(struct iperf_test *test) +{ + return fflush(test->outfile); +} diff --git a/src/iperf_api.h b/src/iperf_api.h index e04b20f..9840fa0 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -27,6 +27,7 @@ struct iperf_stream; /* short option equivalents, used to support options that only have long form */ #define OPT_SCTP 1 +#define OPT_LOGFILE 2 /* states */ #define TEST_START 1 @@ -225,7 +226,7 @@ int iperf_clearaffinity(struct iperf_test *); /* Custom printf routine. */ int iprintf(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3))); - +int iflush(struct iperf_test *test); /* Error routines. */ void iperf_err(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3))); @@ -251,6 +252,7 @@ enum { IEFILE = 14, // -F file couldn't be opened IEBURST = 15, // Invalid burst count. Maximum value = %dMAX_BURST IEENDCONDITIONS = 16, // Only one test end condition (-t, -n, -k) may be specified + IELOGFILE = 17, // Can't open log file /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 0e0f1ad..16fe942 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -356,7 +356,7 @@ iperf_run_client(struct iperf_test * test) iprintf(test, "%s\n", version); iprintf(test, "%s", ""); fflush(stdout); - printf("%s\n", get_system_info()); + iprintf("%s\n", get_system_info()); } /* Start the client and connect to the server */ @@ -479,5 +479,7 @@ iperf_run_client(struct iperf_test * test) iprintf(test, "%s", report_done); } + iflush(test); + return 0; } diff --git a/src/iperf_error.c b/src/iperf_error.c index d4cb444..51b0f8b 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -28,11 +28,16 @@ iperf_err(struct iperf_test *test, const char *format, ...) if (test != NULL && test->json_output && test->json_top != NULL) cJSON_AddStringToObject(test->json_top, "error", str); else - fprintf(stderr, "iperf3: %s\n", str); + if (test && test->outfile) { + fprintf(test->outfile, "iperf3: %s\n", str); + } + else { + fprintf(stderr, "iperf3: %s\n", str); + } va_end(argp); } -/* Do a printf to stderr, then exit. */ +/* Do a printf to stderr or log file as appropriate, then exit. */ void iperf_errexit(struct iperf_test *test, const char *format, ...) { @@ -45,7 +50,12 @@ iperf_errexit(struct iperf_test *test, const char *format, ...) cJSON_AddStringToObject(test->json_top, "error", str); iperf_json_finish(test); } else - fprintf(stderr, "iperf3: %s\n", str); + if (test && test->outfile) { + fprintf(test->outfile, "iperf3: %s\n", str); + } + else { + fprintf(stderr, "iperf3: %s\n", str); + } va_end(argp); iperf_delete_pidfile(test); exit(1); @@ -116,6 +126,10 @@ iperf_strerror(int i_errno) case IEENDCONDITIONS: snprintf(errstr, len, "only one test end condition (-t, -n, -k) may be specified"); break; + case IELOGFILE: + snprintf(errstr, len, "unable to open log file"); + perr = 1; + break; case IENEWTEST: snprintf(errstr, len, "unable to create a new test"); perr = 1; diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index b21e042..76290ea 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -65,8 +65,8 @@ iperf_server_listen(struct iperf_test *test) } if (!test->json_output) { - printf("-----------------------------------------------------------\n"); - printf("Server listening on %d\n", test->server_port); + iprintf(test, "-----------------------------------------------------------\n"); + iprintf(test, "Server listening on %d\n", test->server_port); } // This needs to be changed to reflect if client has different window size @@ -450,7 +450,7 @@ iperf_run_server(struct iperf_test *test) iprintf(test, "%s\n", version); iprintf(test, "%s", ""); fflush(stdout); - printf("%s\n", get_system_info()); + iprintf(test, "%s\n", get_system_info()); } // Open socket and listen @@ -602,6 +602,8 @@ iperf_run_server(struct iperf_test *test) return -1; } + iflush(test); + if (test->server_affinity != -1) if (iperf_clearaffinity(test) != 0) return -1; diff --git a/src/locale.c b/src/locale.c index 6fa4ade..cb58922 100644 --- a/src/locale.c +++ b/src/locale.c @@ -78,6 +78,7 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n" #endif " -V, --verbose more detailed output\n" " -J, --json output in JSON format\n" + " --logfile f send output to a log file\n" " -d, --debug emit debugging output\n" " -v, --version show version information and quit\n" " -h, --help show this message and quit\n"