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