xref: /iperf/src/iperf_util.c (revision b1bc12a2)
1 /*
2  * iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of
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 [email protected].
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 /* iperf_util.c
28  *
29  * Iperf utility functions
30  *
31  */
32 #include "iperf_config.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <sys/select.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #include <sys/utsname.h>
44 #include <time.h>
45 #include <errno.h>
46 
47 #include "cjson.h"
48 
49 /* make_cookie
50  *
51  * Generate and return a cookie string
52  *
53  * Iperf uses this function to create test "cookies" which
54  * server as unique test identifiers. These cookies are also
55  * used for the authentication of stream connections.
56  */
57 
58 void
59 make_cookie(char *cookie)
60 {
61     static int randomized = 0;
62     char hostname[500];
63     struct timeval tv;
64     char temp[1000];
65 
66     if ( ! randomized )
67         srandom((int) time(0) ^ getpid());
68 
69     /* Generate a string based on hostname, time, randomness, and filler. */
70     (void) gethostname(hostname, sizeof(hostname));
71     (void) gettimeofday(&tv, 0);
72     (void) snprintf(temp, sizeof(temp), "%s.%ld.%06ld.%08lx%08lx.%s", hostname, (unsigned long int) tv.tv_sec, (unsigned long int) tv.tv_usec, (unsigned long int) random(), (unsigned long int) random(), "1234567890123456789012345678901234567890");
73 
74     /* Now truncate it to 36 bytes and terminate. */
75     memcpy(cookie, temp, 36);
76     cookie[36] = '\0';
77 }
78 
79 
80 /* is_closed
81  *
82  * Test if the file descriptor fd is closed.
83  *
84  * Iperf uses this function to test whether a TCP stream socket
85  * is closed, because accepting and denying an invalid connection
86  * in iperf_tcp_accept is not considered an error.
87  */
88 
89 int
90 is_closed(int fd)
91 {
92     struct timeval tv;
93     fd_set readset;
94 
95     FD_ZERO(&readset);
96     FD_SET(fd, &readset);
97     tv.tv_sec = 0;
98     tv.tv_usec = 0;
99 
100     if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
101         if (errno == EBADF)
102             return 1;
103     }
104     return 0;
105 }
106 
107 
108 double
109 timeval_to_double(struct timeval * tv)
110 {
111     double d;
112 
113     d = tv->tv_sec + tv->tv_usec / 1000000;
114 
115     return d;
116 }
117 
118 int
119 timeval_equals(struct timeval * tv0, struct timeval * tv1)
120 {
121     if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
122 	return 1;
123     else
124 	return 0;
125 }
126 
127 double
128 timeval_diff(struct timeval * tv0, struct timeval * tv1)
129 {
130     double time1, time2;
131 
132     time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
133     time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
134 
135     time1 = time1 - time2;
136     if (time1 < 0)
137         time1 = -time1;
138     return time1;
139 }
140 
141 
142 int
143 delay(int64_t ns)
144 {
145     struct timespec req, rem;
146 
147     req.tv_sec = 0;
148 
149     while (ns >= 1000000000L) {
150         ns -= 1000000000L;
151         req.tv_sec += 1;
152     }
153 
154     req.tv_nsec = ns;
155 
156     while (nanosleep(&req, &rem) == -1)
157         if (EINTR == errno)
158             memcpy(&req, &rem, sizeof(rem));
159         else
160             return -1;
161     return 0;
162 }
163 
164 # ifdef DELAY_SELECT_METHOD
165 int
166 delay(int us)
167 {
168     struct timeval tv;
169 
170     tv.tv_sec = 0;
171     tv.tv_usec = us;
172     (void) select(1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv);
173     return 1;
174 }
175 #endif
176 
177 
178 void
179 cpu_util(double pcpu[3])
180 {
181     static struct timeval last;
182     static clock_t clast;
183     static struct rusage rlast;
184     struct timeval temp;
185     clock_t ctemp;
186     struct rusage rtemp;
187     double timediff;
188     double userdiff;
189     double systemdiff;
190 
191     if (pcpu == NULL) {
192         gettimeofday(&last, NULL);
193         clast = clock();
194 	getrusage(RUSAGE_SELF, &rlast);
195         return;
196     }
197 
198     gettimeofday(&temp, NULL);
199     ctemp = clock();
200     getrusage(RUSAGE_SELF, &rtemp);
201 
202     timediff = ((temp.tv_sec * 1000000.0 + temp.tv_usec) -
203                 (last.tv_sec * 1000000.0 + last.tv_usec));
204     userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
205                 (rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
206     systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
207                   (rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
208 
209     pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
210     pcpu[1] = (userdiff / timediff) * 100;
211     pcpu[2] = (systemdiff / timediff) * 100;
212 }
213 
214 const char *
215 get_system_info(void)
216 {
217     static char buf[1024];
218     struct utsname  uts;
219 
220     memset(buf, 0, 1024);
221     uname(&uts);
222 
223     snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
224 	     uts.release, uts.version, uts.machine);
225 
226     return buf;
227 }
228 
229 
230 const char *
231 get_optional_features(void)
232 {
233     static char features[1024];
234     unsigned int numfeatures = 0;
235 
236     snprintf(features, sizeof(features), "Optional features available: ");
237 
238 #if defined(HAVE_CPU_AFFINITY)
239     if (numfeatures > 0) {
240 	strncat(features, ", ",
241 		sizeof(features) - strlen(features) - 1);
242     }
243     strncat(features, "CPU affinity setting",
244 	sizeof(features) - strlen(features) - 1);
245     numfeatures++;
246 #endif /* HAVE_CPU_AFFINITY */
247 
248 #if defined(HAVE_FLOWLABEL)
249     if (numfeatures > 0) {
250 	strncat(features, ", ",
251 		sizeof(features) - strlen(features) - 1);
252     }
253     strncat(features, "IPv6 flow label",
254 	sizeof(features) - strlen(features) - 1);
255     numfeatures++;
256 #endif /* HAVE_FLOWLABEL */
257 
258 #if defined(HAVE_SCTP)
259     if (numfeatures > 0) {
260 	strncat(features, ", ",
261 		sizeof(features) - strlen(features) - 1);
262     }
263     strncat(features, "SCTP",
264 	sizeof(features) - strlen(features) - 1);
265     numfeatures++;
266 #endif /* HAVE_SCTP */
267 
268 #if defined(HAVE_TCP_CONGESTION)
269     if (numfeatures > 0) {
270 	strncat(features, ", ",
271 		sizeof(features) - strlen(features) - 1);
272     }
273     strncat(features, "TCP congestion algorithm setting",
274 	sizeof(features) - strlen(features) - 1);
275     numfeatures++;
276 #endif /* HAVE_TCP_CONGESTION */
277 
278 #if defined(HAVE_SENDFILE)
279     if (numfeatures > 0) {
280 	strncat(features, ", ",
281 		sizeof(features) - strlen(features) - 1);
282     }
283     strncat(features, "sendfile / zerocopy",
284 	sizeof(features) - strlen(features) - 1);
285     numfeatures++;
286 #endif /* HAVE_SENDFILE */
287 
288 #if defined(HAVE_SO_MAX_PACING_RATE)
289     if (numfeatures > 0) {
290 	strncat(features, ", ",
291 		sizeof(features) - strlen(features) - 1);
292     }
293     strncat(features, "socket pacing",
294 	sizeof(features) - strlen(features) - 1);
295     numfeatures++;
296 #endif /* HAVE_SO_MAX_PACING_RATE */
297 
298 #if defined(HAVE_SSL)
299     if (numfeatures > 0) {
300 	strncat(features, ", ",
301 		sizeof(features) - strlen(features) - 1);
302     }
303     strncat(features, "authentication",
304 	sizeof(features) - strlen(features) - 1);
305     numfeatures++;
306 #endif /* HAVE_SSL */
307 
308     if (numfeatures == 0) {
309 	strncat(features, "None",
310 		sizeof(features) - strlen(features) - 1);
311     }
312 
313     return features;
314 }
315 
316 /* Helper routine for building cJSON objects in a printf-like manner.
317 **
318 ** Sample call:
319 **   j = iperf_json_printf("foo: %b  bar: %d  bletch: %f  eep: %s", b, i, f, s);
320 **
321 ** The four formatting characters and the types they expect are:
322 **   %b  boolean           int
323 **   %d  integer           int64_t
324 **   %f  floating point    double
325 **   %s  string            char *
326 ** If the values you're passing in are not these exact types, you must
327 ** cast them, there is no automatic type coercion/widening here.
328 **
329 ** The colons mark the end of field names, and blanks are ignored.
330 **
331 ** This routine is not particularly robust, but it's not part of the API,
332 ** it's just for internal iperf3 use.
333 */
334 cJSON*
335 iperf_json_printf(const char *format, ...)
336 {
337     cJSON* o;
338     va_list argp;
339     const char *cp;
340     char name[100];
341     char* np;
342     cJSON* j;
343 
344     o = cJSON_CreateObject();
345     if (o == NULL)
346         return NULL;
347     va_start(argp, format);
348     np = name;
349     for (cp = format; *cp != '\0'; ++cp) {
350 	switch (*cp) {
351 	    case ' ':
352 	    break;
353 	    case ':':
354 	    *np = '\0';
355 	    break;
356 	    case '%':
357 	    ++cp;
358 	    switch (*cp) {
359 		case 'b':
360 		j = cJSON_CreateBool(va_arg(argp, int));
361 		break;
362 		case 'd':
363 		j = cJSON_CreateNumber(va_arg(argp, int64_t));
364 		break;
365 		case 'f':
366 		j = cJSON_CreateNumber(va_arg(argp, double));
367 		break;
368 		case 's':
369 		j = cJSON_CreateString(va_arg(argp, char *));
370 		break;
371 		default:
372 		va_end(argp);
373 		return NULL;
374 	    }
375 	    if (j == NULL) {
376 	    	va_end(argp);
377 	    	return NULL;
378 	    }
379 	    cJSON_AddItemToObject(o, name, j);
380 	    np = name;
381 	    break;
382 	    default:
383 	    *np++ = *cp;
384 	    break;
385 	}
386     }
387     va_end(argp);
388     return o;
389 }
390 
391 /* Debugging routine to dump out an fd_set. */
392 void
393 iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
394 {
395     int fd;
396     int comma;
397 
398     fprintf(fp, "%s: [", str);
399     comma = 0;
400     for (fd = 0; fd < nfds; ++fd) {
401         if (FD_ISSET(fd, fds)) {
402 	    if (comma)
403 		fprintf(fp, ", ");
404 	    fprintf(fp, "%d", fd);
405 	    comma = 1;
406 	}
407     }
408     fprintf(fp, "]\n");
409 }
410