xref: /iperf/src/iperf_util.c (revision 01fb3e6d)
1 /*
2  * iperf, Copyright (c) 2014, 2016, 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 (numfeatures == 0) {
299 	strncat(features, "None",
300 		sizeof(features) - strlen(features) - 1);
301     }
302 
303     return features;
304 }
305 
306 /* Helper routine for building cJSON objects in a printf-like manner.
307 **
308 ** Sample call:
309 **   j = iperf_json_printf("foo: %b  bar: %d  bletch: %f  eep: %s", b, i, f, s);
310 **
311 ** The four formatting characters and the types they expect are:
312 **   %b  boolean           int
313 **   %d  integer           int64_t
314 **   %f  floating point    double
315 **   %s  string            char *
316 ** If the values you're passing in are not these exact types, you must
317 ** cast them, there is no automatic type coercion/widening here.
318 **
319 ** The colons mark the end of field names, and blanks are ignored.
320 **
321 ** This routine is not particularly robust, but it's not part of the API,
322 ** it's just for internal iperf3 use.
323 */
324 cJSON*
325 iperf_json_printf(const char *format, ...)
326 {
327     cJSON* o;
328     va_list argp;
329     const char *cp;
330     char name[100];
331     char* np;
332     cJSON* j;
333 
334     o = cJSON_CreateObject();
335     if (o == NULL)
336         return NULL;
337     va_start(argp, format);
338     np = name;
339     for (cp = format; *cp != '\0'; ++cp) {
340 	switch (*cp) {
341 	    case ' ':
342 	    break;
343 	    case ':':
344 	    *np = '\0';
345 	    break;
346 	    case '%':
347 	    ++cp;
348 	    switch (*cp) {
349 		case 'b':
350 		j = cJSON_CreateBool(va_arg(argp, int));
351 		break;
352 		case 'd':
353 		j = cJSON_CreateNumber(va_arg(argp, int64_t));
354 		break;
355 		case 'f':
356 		j = cJSON_CreateNumber(va_arg(argp, double));
357 		break;
358 		case 's':
359 		j = cJSON_CreateString(va_arg(argp, char *));
360 		break;
361 		default:
362 		va_end(argp);
363 		return NULL;
364 	    }
365 	    if (j == NULL) {
366 	    	va_end(argp);
367 	    	return NULL;
368 	    }
369 	    cJSON_AddItemToObject(o, name, j);
370 	    np = name;
371 	    break;
372 	    default:
373 	    *np++ = *cp;
374 	    break;
375 	}
376     }
377     va_end(argp);
378     return o;
379 }
380 
381 /* Debugging routine to dump out an fd_set. */
382 void
383 iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
384 {
385     int fd;
386     int comma;
387 
388     fprintf(fp, "%s: [", str);
389     comma = 0;
390     for (fd = 0; fd < nfds; ++fd) {
391         if (FD_ISSET(fd, fds)) {
392 	    if (comma)
393 		fprintf(fp, ", ");
394 	    fprintf(fp, "%d", fd);
395 	    comma = 1;
396 	}
397     }
398     fprintf(fp, "]\n");
399 }
400