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