1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <sys/wait.h>
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 
11 #define NETPERF_CMD	"netperf"
12 #define NETPERF_PATH	"/usr/local/bin/" NETPERF_CMD
13 
14 #define TCP_STRM_FILENAME	"/tmp/tcp_strm.%d.%d"
15 
16 struct netperf_child {
17 	int		fd;
18 };
19 
20 static void
21 usage(const char *cmd)
22 {
23 	fprintf(stderr, "%s -H host [-l len_s] [-i instances] [-r]\n", cmd);
24 	exit(1);
25 }
26 
27 int
28 main(int argc, char *argv[])
29 {
30 	struct netperf_child *instance;
31 	char len_str[32];
32 	char *args[32];
33 	const char *host;
34 	volatile int ninst;
35 	int len, ninst_done;
36 	int opt, i, null_fd, set_minmax = 0;
37 	volatile int reverse = 0;
38 	double result, res_max, res_min, jain;
39 	pid_t mypid;
40 
41 	host = NULL;
42 	ninst = 2;
43 	len = 10;
44 
45 	while ((opt = getopt(argc, argv, "i:H:l:r")) != -1) {
46 		switch (opt) {
47 		case 'i':
48 			ninst = strtoul(optarg, NULL, 10);
49 			break;
50 
51 		case 'H':
52 			host = optarg;
53 			break;
54 
55 		case 'l':
56 			len = strtoul(optarg, NULL, 10);
57 			break;
58 
59 		case 'r':
60 			reverse = 1;
61 			break;
62 
63 		default:
64 			usage(argv[0]);
65 		}
66 	}
67 	if (ninst <= 0 || host == NULL || len <= 0)
68 		usage(argv[0]);
69 
70 	mypid = getpid();
71 
72 	snprintf(len_str, sizeof(len_str), "%d", len);
73 
74 	i = 0;
75 	args[i++] = __DECONST(char *, NETPERF_CMD);
76 	args[i++] = __DECONST(char *, "-P0");
77 	args[i++] = __DECONST(char *, "-H");
78 	args[i++] = __DECONST(char *, host);
79 	args[i++] = __DECONST(char *, "-l");
80 	args[i++] = __DECONST(char *, len_str);
81 	args[i++] = __DECONST(char *, "-t");
82 	if (reverse)
83 		args[i++] = __DECONST(char *, "TCP_MAERTS");
84 	else
85 		args[i++] = __DECONST(char *, "TCP_STREAM");
86 	args[i] = NULL;
87 
88 	instance = calloc(ninst, sizeof(struct netperf_child));
89 	if (instance == NULL) {
90 		fprintf(stderr, "calloc failed\n");
91 		exit(1);
92 	}
93 
94 	null_fd = open("/dev/null", O_RDWR);
95 	if (null_fd < 0) {
96 		fprintf(stderr, "open null failed: %d\n", errno);
97 		exit(1);
98 	}
99 
100 	for (i = 0; i < ninst; ++i) {
101 		char filename[128];
102 
103 		snprintf(filename, sizeof(filename), TCP_STRM_FILENAME,
104 		    (int)mypid, i);
105 		instance[i].fd = open(filename, O_CREAT | O_TRUNC | O_RDWR,
106 		    S_IWUSR | S_IRUSR);
107 		if (instance[i].fd < 0) {
108 			fprintf(stderr, "open %s failed: %d\n",
109 			    filename, errno);
110 			exit(1);
111 		}
112 	}
113 
114 	for (i = 0; i < ninst; ++i) {
115 		pid_t pid;
116 
117 		pid = vfork();
118 		if (pid == 0) {
119 			int ret;
120 
121 			dup2(instance[i].fd, STDOUT_FILENO);
122 			dup2(null_fd, STDERR_FILENO);
123 			ret = execv(NETPERF_PATH, args);
124 			if (ret < 0) {
125 				fprintf(stderr, "execv %d failed: %d\n",
126 				    i, errno);
127 				_exit(1);
128 			}
129 			/* Never reached */
130 			abort();
131 		} else if (pid < 0) {
132 			fprintf(stderr, "vfork %d failed: %d\n", i, errno);
133 			exit(1);
134 		}
135 	}
136 
137 	ninst_done = 0;
138 	while (ninst_done < ninst) {
139 		pid_t pid;
140 
141 		pid = waitpid(-1, NULL, 0);
142 		if (pid < 0) {
143 			fprintf(stderr, "waitpid failed: %d\n", errno);
144 			exit(1);
145 		}
146 		++ninst_done;
147 	}
148 
149 	res_max = 0.0;
150 	res_min = 0.0;
151 	jain = 0.0;
152 	result = 0.0;
153 	for (i = 0; i < ninst; ++i) {
154 		char line[128], filename[128];
155 		FILE *fp;
156 
157 		close(instance[i].fd);
158 		snprintf(filename, sizeof(filename), TCP_STRM_FILENAME,
159 		    (int)mypid, i);
160 		fp = fopen(filename, "r");
161 		if (fp == NULL) {
162 			fprintf(stderr, "fopen %s failed\n", filename);
163 			exit(1);
164 		}
165 
166 		while (fgets(line, sizeof(line), fp) != NULL) {
167 			int n, arg1, arg2, arg3;
168 			double res, arg4;
169 
170 			n = sscanf(line, "%d%d%d%lf%lf",
171 			    &arg1, &arg2, &arg3, &arg4, &res);
172 			if (n == 5) {
173 				if (!set_minmax) {
174 					res_max = res;
175 					res_min = res;
176 					set_minmax = 1;
177 				} else {
178 					if (res > res_max)
179 						res_max = res;
180 					if (res < res_min)
181 						res_min = res;
182 				}
183 				jain += (res * res);
184 				result += res;
185 				break;
186 			}
187 		}
188 		fclose(fp);
189 		unlink(filename);
190 	}
191 
192 	jain *= ninst;
193 	jain = (result * result) / jain;
194 
195 	printf("%s %.2f Mbps\n", reverse ? "TCP_MAERTS" : "TCP_STREAM", result);
196 	printf("min/max (jain) %.2f Mbps/%.2f Mbps (%f)\n",
197 	    res_min, res_max, jain);
198 
199 	exit(0);
200 }
201