Annotation of embedaddon/iperf/src/iperf_client_api.c, revision 1.1.1.2

1.1       misho       1: /*
1.1.1.2 ! misho       2:  * iperf, Copyright (c) 2014-2020, The Regents of the University of
1.1       misho       3:  * California, through Lawrence Berkeley National Laboratory (subject
                      4:  * to receipt of any required approvals from the U.S. Dept. of
                      5:  * Energy).  All rights reserved.
                      6:  *
                      7:  * If you have questions about your rights to use or distribute this
                      8:  * software, please contact Berkeley Lab's Technology Transfer
                      9:  * Department at TTD@lbl.gov.
                     10:  *
                     11:  * NOTICE.  This software is owned by the U.S. Department of Energy.
                     12:  * As such, the U.S. Government has been granted for itself and others
                     13:  * acting on its behalf a paid-up, nonexclusive, irrevocable,
                     14:  * worldwide license in the Software to reproduce, prepare derivative
                     15:  * works, and perform publicly and display publicly.  Beginning five
                     16:  * (5) years after the date permission to assert copyright is obtained
                     17:  * from the U.S. Department of Energy, and subject to any subsequent
                     18:  * five (5) year renewals, the U.S. Government is granted for itself
                     19:  * and others acting on its behalf a paid-up, nonexclusive,
                     20:  * irrevocable, worldwide license in the Software to reproduce,
                     21:  * prepare derivative works, distribute copies to the public, perform
                     22:  * publicly and display publicly, and to permit others to do so.
                     23:  *
                     24:  * This code is distributed under a BSD style license, see the LICENSE
                     25:  * file for complete information.
                     26:  */
                     27: #include <errno.h>
                     28: #include <setjmp.h>
                     29: #include <stdio.h>
                     30: #include <stdlib.h>
                     31: #include <string.h>
                     32: #include <unistd.h>
                     33: #include <signal.h>
                     34: #include <sys/types.h>
1.1.1.2 ! misho      35: #include <netinet/in.h>
1.1       misho      36: #include <sys/select.h>
                     37: #include <sys/uio.h>
                     38: #include <arpa/inet.h>
                     39: 
                     40: #include "iperf.h"
                     41: #include "iperf_api.h"
                     42: #include "iperf_util.h"
                     43: #include "iperf_locale.h"
1.1.1.2 ! misho      44: #include "iperf_time.h"
1.1       misho      45: #include "net.h"
                     46: #include "timer.h"
                     47: 
1.1.1.2 ! misho      48: #if defined(HAVE_TCP_CONGESTION)
        !            49: #if !defined(TCP_CA_NAME_MAX)
        !            50: #define TCP_CA_NAME_MAX 16
        !            51: #endif /* TCP_CA_NAME_MAX */
        !            52: #endif /* HAVE_TCP_CONGESTION */
1.1       misho      53: 
                     54: int
1.1.1.2 ! misho      55: iperf_create_streams(struct iperf_test *test, int sender)
1.1       misho      56: {
                     57:     int i, s;
1.1.1.2 ! misho      58: #if defined(HAVE_TCP_CONGESTION)
        !            59:     int saved_errno;
        !            60: #endif /* HAVE_TCP_CONGESTION */
1.1       misho      61:     struct iperf_stream *sp;
                     62: 
                     63:     int orig_bind_port = test->bind_port;
                     64:     for (i = 0; i < test->num_streams; ++i) {
                     65: 
                     66:         test->bind_port = orig_bind_port;
                     67:        if (orig_bind_port)
                     68:            test->bind_port += i;
                     69:         if ((s = test->protocol->connect(test)) < 0)
                     70:             return -1;
                     71: 
1.1.1.2 ! misho      72: #if defined(HAVE_TCP_CONGESTION)
        !            73:        if (test->protocol->id == Ptcp) {
        !            74:            if (test->congestion) {
        !            75:                if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
        !            76:                    saved_errno = errno;
        !            77:                    close(s);
        !            78:                    errno = saved_errno;
        !            79:                    i_errno = IESETCONGESTION;
        !            80:                    return -1;
        !            81:                } 
        !            82:            }
        !            83:            {
        !            84:                socklen_t len = TCP_CA_NAME_MAX;
        !            85:                char ca[TCP_CA_NAME_MAX + 1];
        !            86:                if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) {
        !            87:                    saved_errno = errno;
        !            88:                    close(s);
        !            89:                    errno = saved_errno;
        !            90:                    i_errno = IESETCONGESTION;
        !            91:                    return -1;
        !            92:                }
        !            93:                test->congestion_used = strdup(ca);
        !            94:                if (test->debug) {
        !            95:                    printf("Congestion algorithm is %s\n", test->congestion_used);
        !            96:                }
        !            97:            }
        !            98:        }
        !            99: #endif /* HAVE_TCP_CONGESTION */
        !           100: 
        !           101:        if (sender)
1.1       misho     102:            FD_SET(s, &test->write_set);
                    103:        else
                    104:            FD_SET(s, &test->read_set);
                    105:        if (s > test->max_fd) test->max_fd = s;
                    106: 
1.1.1.2 ! misho     107:         sp = iperf_new_stream(test, s, sender);
1.1       misho     108:         if (!sp)
                    109:             return -1;
                    110: 
                    111:         /* Perform the new stream callback */
                    112:         if (test->on_new_stream)
                    113:             test->on_new_stream(sp);
                    114:     }
                    115: 
                    116:     return 0;
                    117: }
                    118: 
                    119: static void
1.1.1.2 ! misho     120: test_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
1.1       misho     121: {
                    122:     struct iperf_test *test = client_data.p;
                    123: 
                    124:     test->timer = NULL;
                    125:     test->done = 1;
                    126: }
                    127: 
                    128: static void
1.1.1.2 ! misho     129: client_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
1.1       misho     130: {
                    131:     struct iperf_test *test = client_data.p;
                    132: 
                    133:     if (test->done)
                    134:         return;
                    135:     if (test->stats_callback)
                    136:        test->stats_callback(test);
                    137: }
                    138: 
                    139: static void
1.1.1.2 ! misho     140: client_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
1.1       misho     141: {
                    142:     struct iperf_test *test = client_data.p;
                    143: 
                    144:     if (test->done)
                    145:         return;
                    146:     if (test->reporter_callback)
                    147:        test->reporter_callback(test);
                    148: }
                    149: 
                    150: static int
                    151: create_client_timers(struct iperf_test * test)
                    152: {
1.1.1.2 ! misho     153:     struct iperf_time now;
1.1       misho     154:     TimerClientData cd;
                    155: 
1.1.1.2 ! misho     156:     if (iperf_time_now(&now) < 0) {
1.1       misho     157:        i_errno = IEINITTEST;
                    158:        return -1;
                    159:     }
                    160:     cd.p = test;
                    161:     test->timer = test->stats_timer = test->reporter_timer = NULL;
                    162:     if (test->duration != 0) {
                    163:        test->done = 0;
                    164:         test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0);
                    165:         if (test->timer == NULL) {
                    166:             i_errno = IEINITTEST;
                    167:             return -1;
                    168:        }
                    169:     } 
                    170:     if (test->stats_interval != 0) {
                    171:         test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
                    172:         if (test->stats_timer == NULL) {
                    173:             i_errno = IEINITTEST;
                    174:             return -1;
                    175:        }
                    176:     }
                    177:     if (test->reporter_interval != 0) {
                    178:         test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
                    179:         if (test->reporter_timer == NULL) {
                    180:             i_errno = IEINITTEST;
                    181:             return -1;
                    182:        }
                    183:     }
                    184:     return 0;
                    185: }
                    186: 
                    187: static void
1.1.1.2 ! misho     188: client_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
1.1       misho     189: {
                    190:     struct iperf_test *test = client_data.p;
                    191: 
                    192:     test->omit_timer = NULL;
                    193:     test->omitting = 0;
                    194:     iperf_reset_stats(test);
                    195:     if (test->verbose && !test->json_output && test->reporter_interval == 0)
1.1.1.2 ! misho     196:         iperf_printf(test, "%s", report_omit_done);
1.1       misho     197: 
                    198:     /* Reset the timers. */
                    199:     if (test->stats_timer != NULL)
                    200:         tmr_reset(nowP, test->stats_timer);
                    201:     if (test->reporter_timer != NULL)
                    202:         tmr_reset(nowP, test->reporter_timer);
                    203: }
                    204: 
                    205: static int
                    206: create_client_omit_timer(struct iperf_test * test)
                    207: {
1.1.1.2 ! misho     208:     struct iperf_time now;
1.1       misho     209:     TimerClientData cd;
                    210: 
                    211:     if (test->omit == 0) {
                    212:        test->omit_timer = NULL;
                    213:         test->omitting = 0;
                    214:     } else {
1.1.1.2 ! misho     215:        if (iperf_time_now(&now) < 0) {
1.1       misho     216:            i_errno = IEINITTEST;
                    217:            return -1;
                    218:        }
                    219:        test->omitting = 1;
                    220:        cd.p = test;
                    221:        test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
                    222:        if (test->omit_timer == NULL) {
                    223:            i_errno = IEINITTEST;
                    224:            return -1;
                    225:        }
                    226:     }
                    227:     return 0;
                    228: }
                    229: 
                    230: int
                    231: iperf_handle_message_client(struct iperf_test *test)
                    232: {
                    233:     int rval;
                    234:     int32_t err;
                    235: 
                    236:     /*!!! Why is this read() and not Nread()? */
                    237:     if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
                    238:         if (rval == 0) {
                    239:             i_errno = IECTRLCLOSE;
                    240:             return -1;
                    241:         } else {
                    242:             i_errno = IERECVMESSAGE;
                    243:             return -1;
                    244:         }
                    245:     }
                    246: 
                    247:     switch (test->state) {
                    248:         case PARAM_EXCHANGE:
                    249:             if (iperf_exchange_parameters(test) < 0)
                    250:                 return -1;
                    251:             if (test->on_connect)
                    252:                 test->on_connect(test);
                    253:             break;
                    254:         case CREATE_STREAMS:
1.1.1.2 ! misho     255:             if (test->mode == BIDIRECTIONAL)
        !           256:             {
        !           257:                 if (iperf_create_streams(test, 1) < 0)
        !           258:                     return -1;
        !           259:                 if (iperf_create_streams(test, 0) < 0)
        !           260:                     return -1;
        !           261:             }
        !           262:             else if (iperf_create_streams(test, test->mode) < 0)
1.1       misho     263:                 return -1;
                    264:             break;
                    265:         case TEST_START:
                    266:             if (iperf_init_test(test) < 0)
                    267:                 return -1;
                    268:             if (create_client_timers(test) < 0)
                    269:                 return -1;
                    270:             if (create_client_omit_timer(test) < 0)
                    271:                 return -1;
1.1.1.2 ! misho     272:            if (test->mode)
1.1       misho     273:                if (iperf_create_send_timers(test) < 0)
                    274:                    return -1;
                    275:             break;
                    276:         case TEST_RUNNING:
                    277:             break;
                    278:         case EXCHANGE_RESULTS:
                    279:             if (iperf_exchange_results(test) < 0)
                    280:                 return -1;
                    281:             break;
                    282:         case DISPLAY_RESULTS:
                    283:             if (test->on_test_finish)
                    284:                 test->on_test_finish(test);
                    285:             iperf_client_end(test);
                    286:             break;
                    287:         case IPERF_DONE:
                    288:             break;
                    289:         case SERVER_TERMINATE:
                    290:             i_errno = IESERVERTERM;
                    291: 
                    292:            /*
                    293:             * Temporarily be in DISPLAY_RESULTS phase so we can get
                    294:             * ending summary statistics.
                    295:             */
                    296:            signed char oldstate = test->state;
                    297:            cpu_util(test->cpu_util);
                    298:            test->state = DISPLAY_RESULTS;
                    299:            test->reporter_callback(test);
                    300:            test->state = oldstate;
                    301:             return -1;
                    302:         case ACCESS_DENIED:
                    303:             i_errno = IEACCESSDENIED;
                    304:             return -1;
                    305:         case SERVER_ERROR:
                    306:             if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
                    307:                 i_errno = IECTRLREAD;
                    308:                 return -1;
                    309:             }
                    310:            i_errno = ntohl(err);
                    311:             if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
                    312:                 i_errno = IECTRLREAD;
                    313:                 return -1;
                    314:             }
                    315:             errno = ntohl(err);
                    316:             return -1;
                    317:         default:
                    318:             i_errno = IEMESSAGE;
                    319:             return -1;
                    320:     }
                    321: 
                    322:     return 0;
                    323: }
                    324: 
                    325: 
                    326: 
                    327: /* iperf_connect -- client to server connection function */
                    328: int
                    329: iperf_connect(struct iperf_test *test)
                    330: {
                    331:     FD_ZERO(&test->read_set);
                    332:     FD_ZERO(&test->write_set);
                    333: 
                    334:     make_cookie(test->cookie);
                    335: 
                    336:     /* Create and connect the control channel */
                    337:     if (test->ctrl_sck < 0)
                    338:        // Create the control channel using an ephemeral port
1.1.1.2 ! misho     339:        test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
1.1       misho     340:     if (test->ctrl_sck < 0) {
                    341:         i_errno = IECONNECT;
                    342:         return -1;
                    343:     }
                    344: 
                    345:     if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
                    346:         i_errno = IESENDCOOKIE;
                    347:         return -1;
                    348:     }
                    349: 
                    350:     FD_SET(test->ctrl_sck, &test->read_set);
                    351:     if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
                    352: 
1.1.1.2 ! misho     353:     int opt;
        !           354:     socklen_t len;
        !           355: 
        !           356:     len = sizeof(opt);
        !           357:     if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
        !           358:         test->ctrl_sck_mss = 0;
        !           359:     }
        !           360:     else {
        !           361:         if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) {
        !           362:             test->ctrl_sck_mss = opt;
        !           363:         }
        !           364:         else {
        !           365:             char str[128];
        !           366:             snprintf(str, sizeof(str),
        !           367:                      "Ignoring nonsense TCP MSS %d", opt);
        !           368:             warning(str);
        !           369: 
        !           370:             test->ctrl_sck_mss = 0;
        !           371:         }
        !           372:     }
        !           373: 
        !           374:     if (test->verbose) {
        !           375:        printf("Control connection MSS %d\n", test->ctrl_sck_mss);
        !           376:     }
        !           377: 
        !           378:     /*
        !           379:      * If we're doing a UDP test and the block size wasn't explicitly
        !           380:      * set, then use the known MSS of the control connection to pick
        !           381:      * an appropriate default.  If we weren't able to get the
        !           382:      * MSS for some reason, then default to something that should
        !           383:      * work on non-jumbo-frame Ethernet networks.  The goal is to
        !           384:      * pick a reasonable default that is large but should get from
        !           385:      * sender to receiver without any IP fragmentation.
        !           386:      *
        !           387:      * We assume that the control connection is routed the same as the
        !           388:      * data packets (thus has the same PMTU).  Also in the case of
        !           389:      * --reverse tests, we assume that the MTU is the same in both
        !           390:      * directions.  Note that even if the algorithm guesses wrong,
        !           391:      * the user always has the option to override.
        !           392:      */
        !           393:     if (test->protocol->id == Pudp) {
        !           394:        if (test->settings->blksize == 0) {
        !           395:            if (test->ctrl_sck_mss) {
        !           396:                test->settings->blksize = test->ctrl_sck_mss;
        !           397:            }
        !           398:            else {
        !           399:                test->settings->blksize = DEFAULT_UDP_BLKSIZE;
        !           400:            }
        !           401:            if (test->verbose) {
        !           402:                printf("Setting UDP block size to %d\n", test->settings->blksize);
        !           403:            }
        !           404:        }
        !           405: 
        !           406:        /*
        !           407:         * Regardless of whether explicitly or implicitly set, if the
        !           408:         * block size is larger than the MSS, print a warning.
        !           409:         */
        !           410:        if (test->ctrl_sck_mss > 0 &&
        !           411:            test->settings->blksize > test->ctrl_sck_mss) {
        !           412:            char str[128];
        !           413:            snprintf(str, sizeof(str),
        !           414:                     "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss);
        !           415:            warning(str);
        !           416:        }
        !           417:     }
        !           418: 
1.1       misho     419:     return 0;
                    420: }
                    421: 
                    422: 
                    423: int
                    424: iperf_client_end(struct iperf_test *test)
                    425: {
                    426:     struct iperf_stream *sp;
                    427: 
                    428:     /* Close all stream sockets */
                    429:     SLIST_FOREACH(sp, &test->streams, streams) {
                    430:         close(sp->socket);
                    431:     }
                    432: 
                    433:     /* show final summary */
                    434:     test->reporter_callback(test);
                    435: 
                    436:     if (iperf_set_send_state(test, IPERF_DONE) != 0)
                    437:         return -1;
                    438: 
1.1.1.2 ! misho     439:     /* Close control socket */
        !           440:     if (test->ctrl_sck)
        !           441:         close(test->ctrl_sck);
        !           442: 
1.1       misho     443:     return 0;
                    444: }
                    445: 
                    446: 
                    447: int
                    448: iperf_run_client(struct iperf_test * test)
                    449: {
                    450:     int startup;
                    451:     int result = 0;
                    452:     fd_set read_set, write_set;
1.1.1.2 ! misho     453:     struct iperf_time now;
1.1       misho     454:     struct timeval* timeout = NULL;
                    455:     struct iperf_stream *sp;
                    456: 
1.1.1.2 ! misho     457:     if (test->logfile)
        !           458:         if (iperf_open_logfile(test) < 0)
        !           459:             return -1;
        !           460: 
1.1       misho     461:     if (test->affinity != -1)
                    462:        if (iperf_setaffinity(test, test->affinity) != 0)
                    463:            return -1;
                    464: 
                    465:     if (test->json_output)
                    466:        if (iperf_json_start(test) < 0)
                    467:            return -1;
                    468: 
                    469:     if (test->json_output) {
                    470:        cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
                    471:        cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
                    472:     } else if (test->verbose) {
1.1.1.2 ! misho     473:        iperf_printf(test, "%s\n", version);
        !           474:        iperf_printf(test, "%s", "");
        !           475:        iperf_printf(test, "%s\n", get_system_info());
1.1       misho     476:        iflush(test);
                    477:     }
                    478: 
                    479:     /* Start the client and connect to the server */
                    480:     if (iperf_connect(test) < 0)
1.1.1.2 ! misho     481:         goto cleanup_and_fail;
1.1       misho     482: 
                    483:     /* Begin calculating CPU utilization */
                    484:     cpu_util(NULL);
                    485: 
                    486:     startup = 1;
                    487:     while (test->state != IPERF_DONE) {
                    488:        memcpy(&read_set, &test->read_set, sizeof(fd_set));
                    489:        memcpy(&write_set, &test->write_set, sizeof(fd_set));
1.1.1.2 ! misho     490:        iperf_time_now(&now);
1.1       misho     491:        timeout = tmr_timeout(&now);
                    492:        result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
                    493:        if (result < 0 && errno != EINTR) {
                    494:            i_errno = IESELECT;
1.1.1.2 ! misho     495:            goto cleanup_and_fail;
1.1       misho     496:        }
                    497:        if (result > 0) {
                    498:            if (FD_ISSET(test->ctrl_sck, &read_set)) {
                    499:                if (iperf_handle_message_client(test) < 0) {
1.1.1.2 ! misho     500:                    goto cleanup_and_fail;
1.1       misho     501:                }
                    502:                FD_CLR(test->ctrl_sck, &read_set);
                    503:            }
                    504:        }
                    505: 
                    506:        if (test->state == TEST_RUNNING) {
                    507: 
                    508:            /* Is this our first time really running? */
                    509:            if (startup) {
                    510:                startup = 0;
                    511: 
                    512:                // Set non-blocking for non-UDP tests
                    513:                if (test->protocol->id != Pudp) {
                    514:                    SLIST_FOREACH(sp, &test->streams, streams) {
                    515:                        setnonblocking(sp->socket, 1);
                    516:                    }
                    517:                }
                    518:            }
                    519: 
1.1.1.2 ! misho     520: 
        !           521:            if (test->mode == BIDIRECTIONAL)
        !           522:            {
        !           523:                 if (iperf_send(test, &write_set) < 0)
        !           524:                     goto cleanup_and_fail;
        !           525:                 if (iperf_recv(test, &read_set) < 0)
        !           526:                     goto cleanup_and_fail;
        !           527:            } else if (test->mode == SENDER) {
        !           528:                 // Regular mode. Client sends.
        !           529:                 if (iperf_send(test, &write_set) < 0)
        !           530:                     goto cleanup_and_fail;
1.1       misho     531:            } else {
1.1.1.2 ! misho     532:                 // Reverse mode. Client receives.
        !           533:                 if (iperf_recv(test, &read_set) < 0)
        !           534:                     goto cleanup_and_fail;
1.1       misho     535:            }
                    536: 
1.1.1.2 ! misho     537: 
1.1       misho     538:             /* Run the timers. */
1.1.1.2 ! misho     539:             iperf_time_now(&now);
1.1       misho     540:             tmr_run(&now);
                    541: 
                    542:            /* Is the test done yet? */
                    543:            if ((!test->omitting) &&
                    544:                ((test->duration != 0 && test->done) ||
                    545:                 (test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) ||
                    546:                 (test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks))) {
                    547: 
                    548:                // Unset non-blocking for non-UDP tests
                    549:                if (test->protocol->id != Pudp) {
                    550:                    SLIST_FOREACH(sp, &test->streams, streams) {
                    551:                        setnonblocking(sp->socket, 0);
                    552:                    }
                    553:                }
                    554: 
                    555:                /* Yes, done!  Send TEST_END. */
                    556:                test->done = 1;
                    557:                cpu_util(test->cpu_util);
                    558:                test->stats_callback(test);
                    559:                if (iperf_set_send_state(test, TEST_END) != 0)
1.1.1.2 ! misho     560:                     goto cleanup_and_fail;
1.1       misho     561:            }
                    562:        }
                    563:        // If we're in reverse mode, continue draining the data
                    564:        // connection(s) even if test is over.  This prevents a
                    565:        // deadlock where the server side fills up its pipe(s)
                    566:        // and gets blocked, so it can't receive state changes
                    567:        // from the client side.
1.1.1.2 ! misho     568:        else if (test->mode == RECEIVER && test->state == TEST_END) {
1.1       misho     569:            if (iperf_recv(test, &read_set) < 0)
1.1.1.2 ! misho     570:                goto cleanup_and_fail;
1.1       misho     571:        }
                    572:     }
                    573: 
                    574:     if (test->json_output) {
                    575:        if (iperf_json_finish(test) < 0)
                    576:            return -1;
                    577:     } else {
1.1.1.2 ! misho     578:        iperf_printf(test, "\n");
        !           579:        iperf_printf(test, "%s", report_done);
1.1       misho     580:     }
                    581: 
                    582:     iflush(test);
                    583: 
                    584:     return 0;
1.1.1.2 ! misho     585: 
        !           586:   cleanup_and_fail:
        !           587:     iperf_client_end(test);
        !           588:     if (test->json_output)
        !           589:        iperf_json_finish(test);
        !           590:     iflush(test);
        !           591:     return -1;
1.1       misho     592: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>