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