| /* |
| * iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of |
| * California, through Lawrence Berkeley National Laboratory (subject |
| * to receipt of any required approvals from the U.S. Dept. of |
| * Energy). All rights reserved. |
| * |
| * If you have questions about your rights to use or distribute this |
| * software, please contact Berkeley Lab's Technology Transfer |
| * Department at TTD@lbl.gov. |
| * |
| * NOTICE. This software is owned by the U.S. Department of Energy. |
| * As such, the U.S. Government has been granted for itself and others |
| * acting on its behalf a paid-up, nonexclusive, irrevocable, |
| * worldwide license in the Software to reproduce, prepare derivative |
| * works, and perform publicly and display publicly. Beginning five |
| * (5) years after the date permission to assert copyright is obtained |
| * from the U.S. Department of Energy, and subject to any subsequent |
| * five (5) year renewals, the U.S. Government is granted for itself |
| * and others acting on its behalf a paid-up, nonexclusive, |
| * irrevocable, worldwide license in the Software to reproduce, |
| * prepare derivative works, distribute copies to the public, perform |
| * publicly and display publicly, and to permit others to do so. |
| * |
| * This code is distributed under a BSD style license, see the LICENSE |
| * file for complete information. |
| */ |
| /* iperf_util.c |
| * |
| * Iperf utility functions |
| * |
| */ |
| #include "iperf_config.h" |
| |
| #include <stdio.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <sys/select.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <sys/utsname.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| |
| #include "cjson.h" |
| #include "iperf.h" |
| #include "iperf_api.h" |
| |
| /* |
| * Read entropy from /dev/urandom |
| * Errors are fatal. |
| * Returns 0 on success. |
| */ |
| int readentropy(void *out, size_t outsize) |
| { |
| static FILE *frandom; |
| static const char rndfile[] = "/dev/urandom"; |
| |
| if (!outsize) return 0; |
| |
| if (frandom == NULL) { |
| frandom = fopen(rndfile, "rb"); |
| if (frandom == NULL) { |
| iperf_errexit(NULL, "error - failed to open %s: %s\n", |
| rndfile, strerror(errno)); |
| } |
| setbuf(frandom, NULL); |
| } |
| if (fread(out, 1, outsize, frandom) != outsize) { |
| iperf_errexit(NULL, "error - failed to read %s: %s\n", |
| rndfile, |
| feof(frandom) ? "EOF" : strerror(errno)); |
| } |
| return 0; |
| } |
| |
| |
| /* |
| * Fills buffer with repeating pattern (similar to pattern that used in iperf2) |
| */ |
| void fill_with_repeating_pattern(void *out, size_t outsize) |
| { |
| size_t i; |
| int counter = 0; |
| char *buf = (char *)out; |
| |
| if (!outsize) return; |
| |
| for (i = 0; i < outsize; i++) { |
| buf[i] = (char)('0' + counter); |
| if (counter >= 9) |
| counter = 0; |
| else |
| counter++; |
| } |
| } |
| |
| |
| /* make_cookie |
| * |
| * Generate and return a cookie string |
| * |
| * Iperf uses this function to create test "cookies" which |
| * server as unique test identifiers. These cookies are also |
| * used for the authentication of stream connections. |
| * Assumes cookie has size (COOKIE_SIZE + 1) char's. |
| */ |
| |
| void |
| make_cookie(const char *cookie) |
| { |
| unsigned char *out = (unsigned char*)cookie; |
| size_t pos; |
| static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567"; |
| |
| readentropy(out, COOKIE_SIZE); |
| for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) { |
| out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)]; |
| } |
| out[pos] = '\0'; |
| } |
| |
| |
| /* is_closed |
| * |
| * Test if the file descriptor fd is closed. |
| * |
| * Iperf uses this function to test whether a TCP stream socket |
| * is closed, because accepting and denying an invalid connection |
| * in iperf_tcp_accept is not considered an error. |
| */ |
| |
| int |
| is_closed(int fd) |
| { |
| struct timeval tv; |
| fd_set readset; |
| |
| FD_ZERO(&readset); |
| FD_SET(fd, &readset); |
| tv.tv_sec = 0; |
| tv.tv_usec = 0; |
| |
| if (select(fd+1, &readset, NULL, NULL, &tv) < 0) { |
| if (errno == EBADF) |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| double |
| timeval_to_double(struct timeval * tv) |
| { |
| double d; |
| |
| d = tv->tv_sec + tv->tv_usec / 1000000; |
| |
| return d; |
| } |
| |
| int |
| timeval_equals(struct timeval * tv0, struct timeval * tv1) |
| { |
| if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec ) |
| return 1; |
| else |
| return 0; |
| } |
| |
| double |
| timeval_diff(struct timeval * tv0, struct timeval * tv1) |
| { |
| double time1, time2; |
| |
| time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0); |
| time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0); |
| |
| time1 = time1 - time2; |
| if (time1 < 0) |
| time1 = -time1; |
| return time1; |
| } |
| |
| void |
| cpu_util(double pcpu[3]) |
| { |
| static struct iperf_time last; |
| static clock_t clast; |
| static struct rusage rlast; |
| struct iperf_time now, temp_time; |
| clock_t ctemp; |
| struct rusage rtemp; |
| double timediff; |
| double userdiff; |
| double systemdiff; |
| |
| if (pcpu == NULL) { |
| iperf_time_now(&last); |
| clast = clock(); |
| getrusage(RUSAGE_SELF, &rlast); |
| return; |
| } |
| |
| iperf_time_now(&now); |
| ctemp = clock(); |
| getrusage(RUSAGE_SELF, &rtemp); |
| |
| iperf_time_diff(&now, &last, &temp_time); |
| timediff = iperf_time_in_usecs(&temp_time); |
| |
| userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) - |
| (rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec)); |
| systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) - |
| (rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec)); |
| |
| pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100; |
| pcpu[1] = (userdiff / timediff) * 100; |
| pcpu[2] = (systemdiff / timediff) * 100; |
| } |
| |
| const char * |
| get_system_info(void) |
| { |
| static char buf[1024]; |
| struct utsname uts; |
| |
| memset(buf, 0, 1024); |
| uname(&uts); |
| |
| snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename, |
| uts.release, uts.version, uts.machine); |
| |
| return buf; |
| } |
| |
| |
| const char * |
| get_optional_features(void) |
| { |
| static char features[1024]; |
| unsigned int numfeatures = 0; |
| |
| snprintf(features, sizeof(features), "Optional features available: "); |
| |
| #if defined(HAVE_CPU_AFFINITY) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "CPU affinity setting", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_CPU_AFFINITY */ |
| |
| #if defined(HAVE_FLOWLABEL) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "IPv6 flow label", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_FLOWLABEL */ |
| |
| #if defined(HAVE_SCTP_H) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "SCTP", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_SCTP_H */ |
| |
| #if defined(HAVE_TCP_CONGESTION) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "TCP congestion algorithm setting", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_TCP_CONGESTION */ |
| |
| #if defined(HAVE_SENDFILE) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "sendfile / zerocopy", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_SENDFILE */ |
| |
| #if defined(HAVE_SO_MAX_PACING_RATE) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "socket pacing", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_SO_MAX_PACING_RATE */ |
| |
| #if defined(HAVE_SSL) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "authentication", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_SSL */ |
| |
| #if defined(HAVE_SO_BINDTODEVICE) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "bind to device", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_SO_BINDTODEVICE */ |
| |
| #if defined(HAVE_DONT_FRAGMENT) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "support IPv4 don't fragment", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_DONT_FRAGMENT */ |
| |
| #if defined(HAVE_PTHREAD) |
| if (numfeatures > 0) { |
| strncat(features, ", ", |
| sizeof(features) - strlen(features) - 1); |
| } |
| strncat(features, "POSIX threads", |
| sizeof(features) - strlen(features) - 1); |
| numfeatures++; |
| #endif /* HAVE_PTHREAD */ |
| |
| if (numfeatures == 0) { |
| strncat(features, "None", |
| sizeof(features) - strlen(features) - 1); |
| } |
| |
| return features; |
| } |
| |
| /* 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_CreateNumber(va_arg(argp, int64_t)); |
| break; |
| case 'f': |
| j = cJSON_CreateNumber(va_arg(argp, double)); |
| break; |
| case 's': |
| j = cJSON_CreateString(va_arg(argp, char *)); |
| break; |
| default: |
| va_end(argp); |
| return NULL; |
| } |
| if (j == NULL) { |
| va_end(argp); |
| return NULL; |
| } |
| cJSON_AddItemToObject(o, name, j); |
| np = name; |
| break; |
| default: |
| *np++ = *cp; |
| break; |
| } |
| } |
| va_end(argp); |
| return o; |
| } |
| |
| /* Debugging routine to dump out an fd_set. */ |
| void |
| iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds) |
| { |
| int fd; |
| int comma; |
| |
| fprintf(fp, "%s: [", str); |
| comma = 0; |
| for (fd = 0; fd < nfds; ++fd) { |
| if (FD_ISSET(fd, fds)) { |
| if (comma) |
| fprintf(fp, ", "); |
| fprintf(fp, "%d", fd); |
| comma = 1; |
| } |
| } |
| fprintf(fp, "]\n"); |
| } |
| |
| /* |
| * daemon(3) implementation for systems lacking one. |
| * Cobbled together from various daemon(3) implementations, |
| * not intended to be general-purpose. */ |
| #ifndef HAVE_DAEMON |
| int daemon(int nochdir, int noclose) |
| { |
| pid_t pid = 0; |
| pid_t sid = 0; |
| int fd; |
| |
| /* |
| * Ignore any possible SIGHUP when the parent process exits. |
| * Note that the iperf3 server process will eventually install |
| * its own signal handler for SIGHUP, so we can be a little |
| * sloppy about not restoring the prior value. This does not |
| * generalize. |
| */ |
| signal(SIGHUP, SIG_IGN); |
| |
| pid = fork(); |
| if (pid < 0) { |
| return -1; |
| } |
| if (pid > 0) { |
| /* Use _exit() to avoid doing atexit() stuff. */ |
| _exit(0); |
| } |
| |
| sid = setsid(); |
| if (sid < 0) { |
| return -1; |
| } |
| |
| /* |
| * Fork again to avoid becoming a session leader. |
| * This might only matter on old SVr4-derived OSs. |
| * Note in particular that glibc and FreeBSD libc |
| * only fork once. |
| */ |
| pid = fork(); |
| if (pid == -1) { |
| return -1; |
| } else if (pid != 0) { |
| _exit(0); |
| } |
| |
| if (!nochdir) { |
| chdir("/"); |
| } |
| |
| if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { |
| dup2(fd, STDIN_FILENO); |
| dup2(fd, STDOUT_FILENO); |
| dup2(fd, STDERR_FILENO); |
| if (fd > 2) { |
| close(fd); |
| } |
| } |
| return (0); |
| } |
| #endif /* HAVE_DAEMON */ |
| |
| /* Compatibility version of getline(3) for systems that don't have it.. */ |
| #ifndef HAVE_GETLINE |
| /* The following code adopted from NetBSD's getline.c, which is: */ |
| |
| /*- |
| * Copyright (c) 2011 The NetBSD Foundation, Inc. |
| * All rights reserved. |
| * |
| * This code is derived from software contributed to The NetBSD Foundation |
| * by Christos Zoulas. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| ssize_t |
| getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) |
| { |
| char *ptr, *eptr; |
| |
| |
| if (*buf == NULL || *bufsiz == 0) { |
| *bufsiz = BUFSIZ; |
| if ((*buf = malloc(*bufsiz)) == NULL) |
| return -1; |
| } |
| |
| for (ptr = *buf, eptr = *buf + *bufsiz;;) { |
| int c = fgetc(fp); |
| if (c == -1) { |
| if (feof(fp)) { |
| ssize_t diff = (ssize_t)(ptr - *buf); |
| if (diff != 0) { |
| *ptr = '\0'; |
| return diff; |
| } |
| } |
| return -1; |
| } |
| *ptr++ = c; |
| if (c == delimiter) { |
| *ptr = '\0'; |
| return ptr - *buf; |
| } |
| if (ptr + 2 >= eptr) { |
| char *nbuf; |
| size_t nbufsiz = *bufsiz * 2; |
| ssize_t d = ptr - *buf; |
| if ((nbuf = realloc(*buf, nbufsiz)) == NULL) |
| return -1; |
| *buf = nbuf; |
| *bufsiz = nbufsiz; |
| eptr = nbuf + nbufsiz; |
| ptr = nbuf + d; |
| } |
| } |
| } |
| |
| ssize_t |
| getline(char **buf, size_t *bufsiz, FILE *fp) |
| { |
| return getdelim(buf, bufsiz, '\n', fp); |
| } |
| |
| #endif |