xref: /iperf/src/iperf_util.c (revision ad319fac)
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 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #include <sys/select.h>
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/utsname.h>
26 #include <time.h>
27 #include <errno.h>
28 
29 #include "config.h"
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) / timediff) * 100;
193     pcpu[1] = (userdiff / timediff) * 100;
194     pcpu[2] = (systemdiff / timediff) * 100;
195 }
196 
197 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 /* Helper routine for building cJSON objects in a printf-like manner.
214 **
215 ** Sample call:
216 **   j = iperf_json_printf("foo: %b  bar: %d  bletch: %f  eep: %s", b, i, f, s);
217 **
218 ** The four formatting characters and the types they expect are:
219 **   %b  boolean           int
220 **   %d  integer           int64_t
221 **   %f  floating point    double
222 **   %s  string            char *
223 ** If the values you're passing in are not these exact types, you must
224 ** cast them, there is no automatic type coercion/widening here.
225 **
226 ** The colons mark the end of field names, and blanks are ignored.
227 **
228 ** This routine is not particularly robust, but it's not part of the API,
229 ** it's just for internal iperf3 use.
230 */
231 cJSON*
232 iperf_json_printf(const char *format, ...)
233 {
234     cJSON* o;
235     va_list argp;
236     const char *cp;
237     char name[100];
238     char* np;
239     cJSON* j;
240 
241     o = cJSON_CreateObject();
242     if (o == NULL)
243         return NULL;
244     va_start(argp, format);
245     np = name;
246     for (cp = format; *cp != '\0'; ++cp) {
247 	switch (*cp) {
248 	    case ' ':
249 	    break;
250 	    case ':':
251 	    *np = '\0';
252 	    break;
253 	    case '%':
254 	    ++cp;
255 	    switch (*cp) {
256 		case 'b':
257 		j = cJSON_CreateBool(va_arg(argp, int));
258 		break;
259 		case 'd':
260 		j = cJSON_CreateInt(va_arg(argp, int64_t));
261 		break;
262 		case 'f':
263 		j = cJSON_CreateFloat(va_arg(argp, double));
264 		break;
265 		case 's':
266 		j = cJSON_CreateString(va_arg(argp, char *));
267 		break;
268 		default:
269 		return NULL;
270 	    }
271 	    if (j == NULL)
272 		return NULL;
273 	    cJSON_AddItemToObject(o, name, j);
274 	    np = name;
275 	    break;
276 	    default:
277 	    *np++ = *cp;
278 	    break;
279 	}
280     }
281     va_end(argp);
282     return o;
283 }
284 
285 /* Debugging routine to dump out an fd_set. */
286 void
287 iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
288 {
289     int fd;
290     int comma;
291 
292     fprintf(fp, "%s: [", str);
293     comma = 0;
294     for (fd = 0; fd < nfds; ++fd) {
295         if (FD_ISSET(fd, fds)) {
296 	    if (comma)
297 		fprintf(fp, ", ");
298 	    fprintf(fp, "%d", fd);
299 	    comma = 1;
300 	}
301     }
302     fprintf(fp, "]\n");
303 }
304